bool QtGraphics_Controls::isNativeControlSupported(ControlType type, ControlPart part)
{ switch (type)
{ case ControlType::Tooltip: case ControlType::Progress: case ControlType::ListNode: return (part == ControlPart::Entire);
case ControlType::Pushbutton: case ControlType::Radiobutton: case ControlType::Checkbox: return (part == ControlPart::Entire) || (part == ControlPart::Focus);
case ControlType::ListHeader: return (part == ControlPart::Button);
case ControlType::Menubar: case ControlType::MenuPopup: case ControlType::Editbox: case ControlType::MultilineEditbox: case ControlType::Combobox: case ControlType::Toolbar: case ControlType::Frame: case ControlType::Scrollbar: case ControlType::WindowBackground: case ControlType::Fixedline: returntrue;
case ControlType::Listbox: return (part == ControlPart::Entire || part == ControlPart::HasBackgroundTexture);
case ControlType::Spinbox: return (part == ControlPart::Entire || part == ControlPart::HasBackgroundTexture);
case ControlType::Slider: return (part == ControlPart::TrackHorzArea || part == ControlPart::TrackVertArea);
case ControlType::TabItem: case ControlType::TabPane: return ((part == ControlPart::Entire) || part == ControlPart::TabPaneWithHeader);
if (m_lastPopupRect.isValid()
&& (type != ControlType::MenuPopup || part != ControlPart::MenuItem))
m_lastPopupRect = QRect();
bool returnVal = true;
QRect widgetRect = toQRect(rControlRegion);
//if no image, or resized, make a new image if (!m_image || m_image->size() != widgetRect.size())
{
m_image.reset(new QImage(widgetRect.width(), widgetRect.height(),
QImage::Format_ARGB32_Premultiplied));
m_image->setDevicePixelRatio(m_rGraphics.devicePixelRatioF());
}
// Default image color - just once switch (type)
{ case ControlType::MenuPopup: if (part == ControlPart::MenuItemCheckMark || part == ControlPart::MenuItemRadioMark)
{ // it is necessary to fill the background transparently first, as this // is painted after menuitem highlight, otherwise there would be a grey area
m_image->fill(Qt::transparent); break;
}
[[fallthrough]]; // QPalette::Window case ControlType::Menubar: case ControlType::WindowBackground:
m_image->fill(QApplication::palette().color(QPalette::Window).rgb()); break; case ControlType::Tooltip:
m_image->fill(QApplication::palette().color(QPalette::ToolTipBase).rgb()); break; case ControlType::Scrollbar: if ((part == ControlPart::DrawBackgroundVert)
|| (part == ControlPart::DrawBackgroundHorz))
{
m_image->fill(QApplication::palette().color(QPalette::Window).rgb()); break;
}
[[fallthrough]]; // Qt::transparent default:
m_image->fill(Qt::transparent); break;
}
if (type == ControlType::Pushbutton)
{ const PushButtonValue& rPBValue = static_cast<const PushButtonValue&>(value); if (part == ControlPart::Focus) // Nothing to do. Drawing focus separately is not needed because that's // already handled by the ControlState::FOCUSED state being set when // drawing the entire control returntrue;
assert(part == ControlPart::Entire);
QStyleOptionButton option; if (nControlState & ControlState::DEFAULT)
option.features |= QStyleOptionButton::DefaultButton; if (rPBValue.m_bFlatButton)
option.features |= QStyleOptionButton::Flat;
draw(QStyle::CE_PushButton, option, m_image.get(), rBackgroundColor,
vclStateValue2StateFlag(nControlState, value));
} elseif (type == ControlType::Menubar)
{ if (part == ControlPart::MenuItem)
{
QStyleOptionMenuItem option;
option.state = vclStateValue2StateFlag(nControlState, value); if ((nControlState & ControlState::ROLLOVER)
&& QApplication::style()->styleHint(QStyle::SH_MenuBar_MouseTracking))
option.state |= QStyle::State_Selected;
if (nControlState
& ControlState::SELECTED) // Passing State_Sunken is currently not documented.
option.state |= QStyle::State_Sunken; // But some kinds of QStyle interpret it.
draw(QStyle::CE_MenuBarItem, option, m_image.get(), rBackgroundColor);
} elseif (part == ControlPart::Entire)
{
QStyleOptionMenuItem option;
draw(QStyle::CE_MenuBarEmptyArea, option, m_image.get(), rBackgroundColor,
vclStateValue2StateFlag(nControlState, value));
} else
{
returnVal = false;
}
} elseif (type == ControlType::MenuPopup)
{
assert(part == ControlPart::MenuItem ? m_lastPopupRect.isValid()
: !m_lastPopupRect.isValid()); if (part == ControlPart::MenuItem)
{
QStyleOptionMenuItem option;
draw(QStyle::CE_MenuItem, option, m_image.get(), rBackgroundColor,
vclStateValue2StateFlag(nControlState, value)); // HACK: LO core first paints the entire popup and only then it paints menu items, // but QMenu::paintEvent() paints popup frame after all items. That means highlighted // items here would paint the highlight over the frame border. Since calls to ControlPart::MenuItem // are always preceded by calls to ControlPart::Entire, just remember the size for the whole // popup (otherwise not possible to get here) and draw the border afterwards.
QRect framerect(m_lastPopupRect.topLeft() - widgetRect.topLeft(),
widgetRect.size().expandedTo(m_lastPopupRect.size()));
QStyleOptionFrame frame;
draw(QStyle::PE_FrameMenu, frame, m_image.get(), rBackgroundColor,
vclStateValue2StateFlag(nControlState, value), framerect);
} elseif (part == ControlPart::Separator)
{
QStyleOptionMenuItem option;
option.menuItemType = QStyleOptionMenuItem::Separator; // Painting the whole menu item area results in different background // with at least Plastique style, so clip only to the separator itself // (QSize( 2, 2 ) is hardcoded in Qt)
option.rect = m_image->rect();
QSize size = sizeFromContents(QStyle::CT_MenuItem, &option, QSize(2, 2));
QRect rect = m_image->rect();
QPoint center = rect.center();
rect.setHeight(size.height());
rect.moveCenter(center);
option.state |= vclStateValue2StateFlag(nControlState, value);
option.rect = rect;
QPainter painter(m_image.get()); // don't paint over popup frame border (like the hack above, but here it can be simpler) constint fw = pixelMetric(QStyle::PM_MenuPanelWidth);
painter.setClipRect(rect.adjusted(fw, 0, -fw, 0));
QApplication::style()->drawControl(QStyle::CE_MenuItem, &option, &painter);
} elseif (part == ControlPart::MenuItemCheckMark || part == ControlPart::MenuItemRadioMark)
{
QStyleOptionMenuItem option;
option.checkType = (part == ControlPart::MenuItemCheckMark)
? QStyleOptionMenuItem::NonExclusive
: QStyleOptionMenuItem::Exclusive;
option.checked = bool(nControlState & ControlState::PRESSED); // widgetRect is now the rectangle for the checkbox/radiobutton itself, but Qt // paints the whole menu item, so translate position (and it'll be clipped); // it is also necessary to fill the background transparently first, as this // is painted after menuitem highlight, otherwise there would be a grey area
assert(value.getType() == ControlType::MenuPopup); const MenupopupValue* menuVal = static_cast<const MenupopupValue*>(&value);
QRect menuItemRect(toQRect(menuVal->maItemRect));
QRect rect(menuItemRect.topLeft() - widgetRect.topLeft(),
widgetRect.size().expandedTo(menuItemRect.size())); // checkboxes are always displayed next to images in menus, so are never centered constint focus_size = pixelMetric(QStyle::PM_FocusFrameHMargin);
rect.moveTo(-focus_size, rect.y());
draw(QStyle::CE_MenuItem, option, m_image.get(), rBackgroundColor,
vclStateValue2StateFlag(nControlState & ~ControlState::PRESSED, value), rect);
} elseif (part == ControlPart::Entire)
{
QStyleOptionMenuItem option;
option.state = vclStateValue2StateFlag(nControlState, value);
draw(QStyle::PE_PanelMenu, option, m_image.get(), rBackgroundColor); // Try hard to get any frame!
QStyleOptionFrame frame;
draw(QStyle::PE_FrameMenu, frame, m_image.get(), rBackgroundColor);
draw(QStyle::PE_FrameWindow, frame, m_image.get(), rBackgroundColor);
m_lastPopupRect = widgetRect;
} else
returnVal = false;
} elseif ((type == ControlType::Toolbar) && (part == ControlPart::Button))
{
QStyleOptionToolButton option;
//if the scroll bar is active (aka not degenerate... allow for hover events) if (sbVal->mnVisibleSize < sbVal->mnMax)
option.state = QStyle::State_MouseOver;
// If the scrollbar has a mnMin == 0 and mnMax == 0 then mnVisibleSize is set to -1?! // I don't know if a negative mnVisibleSize makes any sense, so just handle this case // without crashing LO with a SIGFPE in the Qt library. const tools::Long nVisibleSize
= (sbVal->mnMin == sbVal->mnMax) ? 0 : sbVal->mnVisibleSize;
//setup the active control... always the slider if (sbVal->mnThumbState & ControlState::ROLLOVER)
option.activeSubControls = QStyle::SC_ScrollBarSlider;
// get the overlap size for the tabs, so they will overlap the frame
QStyleOptionTab tabOverlap;
tabOverlap.shape = QTabBar::RoundedNorth;
TabPaneValue::m_nOverlap = pixelMetric(QStyle::PM_TabBarBaseOverlap, &tabOverlap);
switch (type)
{ // Metrics of the push button case ControlType::Pushbutton: if (part == ControlPart::Entire)
{
styleOption.state = vclStateValue2StateFlag(controlState, val);
if (controlState & ControlState::DEFAULT)
{ int size = upscale(pixelMetric(QStyle::PM_ButtonDefaultIndicator, &styleOption),
Round::Ceil);
boundingRect.adjust(-size, -size, size, size);
retVal = true;
}
} elseif (part == ControlPart::Focus)
retVal = true; break; case ControlType::Editbox: case ControlType::MultilineEditbox:
{ // we have to get stable borders, otherwise layout loops. // so we simply only scale the detected borders.
QStyleOptionFrame fo;
fo.frameShape = QFrame::StyledPanel;
fo.state = QStyle::State_Sunken;
fo.lineWidth = pixelMetric(QStyle::PM_DefaultFrameWidth);
fo.rect = downscale(contentRect);
fo.rect.setSize(sizeFromContents(QStyle::CT_LineEdit, &fo, fo.rect.size()));
QRect aSubRect = subElementRect(QStyle::SE_LineEditContents, &fo);
// VCL tests borders with small defaults before layout, where Qt returns no sub-rect, // so this gets us at least some frame. int nLine = upscale(fo.lineWidth, Round::Ceil); int nLeft = qMin(-nLine, upscale(fo.rect.left() - aSubRect.left(), Round::Floor)); int nTop = qMin(-nLine, upscale(fo.rect.top() - aSubRect.top(), Round::Floor)); int nRight = qMax(nLine, upscale(fo.rect.right() - aSubRect.right(), Round::Ceil)); int nBottom = qMax(nLine, upscale(fo.rect.bottom() - aSubRect.bottom(), Round::Ceil));
boundingRect.adjust(nLeft, nTop, nRight, nBottom);
// tdf#150451: ensure a minimum size that fits text content + frame at top and bottom. // Themes may use the widget type for determining the actual frame width to use, // so pass a dummy QLineEdit // // NOTE: This is currently only done here for the minimum size calculation and // not above because the handling for edit boxes here and in the calling code // currently does all kinds of "interesting" things like doing extra size adjustments // or passing the content rect where the bounding rect would be expected,... // Ideally this should be cleaned up in the callers and all platform integrations // to adhere to what the doc in vcl/inc/WidgetDrawInterface.hxx says, but this // here keeps it working with existing code for now. // (s.a. discussion in https://gerrit.libreoffice.org/c/core/+/146516 for more details)
QLineEdit aDummyEdit; constint nFrameWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, &aDummyEdit);
QFontMetrics aFontMetrics(QApplication::font()); constint minHeight = upscale(aFontMetrics.height() + 2 * nFrameWidth, Round::Floor); if (boundingRect.height() < minHeight)
{ constint nDiff = minHeight - boundingRect.height();
boundingRect.setHeight(boundingRect.height() + nDiff);
contentRect.setHeight(contentRect.height() + nDiff);
}
retVal = true; break;
} case ControlType::Checkbox: if (part == ControlPart::Entire)
{
styleOption.state = vclStateValue2StateFlag(controlState, val);
int nWidth = pixelMetric(QStyle::PM_IndicatorWidth, &styleOption); int nHeight = pixelMetric(QStyle::PM_IndicatorHeight, &styleOption);
contentRect.setSize(upscale(QSize(nWidth, nHeight), Round::Ceil));
int nHMargin = pixelMetric(QStyle::PM_FocusFrameHMargin, &styleOption); int nVMargin = pixelMetric(QStyle::PM_FocusFrameVMargin, &styleOption);
contentRect.adjust(0, 0, 2 * upscale(nHMargin, Round::Ceil), 2 * upscale(nVMargin, Round::Ceil));
boundingRect = contentRect;
retVal = true;
} break; case ControlType::Combobox: case ControlType::Listbox:
{
QStyleOptionComboBox cbo;
retVal = true; break;
} case ControlType::Slider:
{ constint w = upscale(pixelMetric(QStyle::PM_SliderLength), Round::Ceil); if (part == ControlPart::ThumbHorz)
{
contentRect
= QRect(boundingRect.left(), boundingRect.top(), w, boundingRect.height());
boundingRect = contentRect;
retVal = true;
} elseif (part == ControlPart::ThumbVert)
{
contentRect
= QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), w);
boundingRect = contentRect;
retVal = true;
} break;
} case ControlType::Toolbar:
{ constint nWorH = upscale(pixelMetric(QStyle::PM_ToolBarHandleExtent), Round::Ceil); if (part == ControlPart::ThumbHorz)
{
contentRect
= QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), nWorH);
boundingRect = contentRect;
retVal = true;
} elseif (part == ControlPart::ThumbVert)
{
contentRect
= QRect(boundingRect.left(), boundingRect.top(), nWorH, boundingRect.height());
boundingRect = contentRect;
retVal = true;
} elseif (part == ControlPart::Button)
{
QStyleOptionToolButton option;
option.arrowType = Qt::NoArrow;
option.features = QStyleOptionToolButton::None;
option.rect = downscale(QRect({ 0, 0 }, contentRect.size()));
contentRect = upscale(
subControlRect(QStyle::CC_ToolButton, &option, QStyle::SC_ToolButton));
boundingRect = contentRect;
retVal = true;
} break;
} case ControlType::Scrollbar:
{ // core can't handle 3-button scrollbars well, so we fix that in hitTestNativeControl(), // for the rest also provide the track area (i.e. area not taken by buttons) if (part == ControlPart::TrackVertArea || part == ControlPart::TrackHorzArea)
{
QStyleOptionSlider option; bool horizontal = (part == ControlPart::TrackHorzArea); //horizontal or vertical
option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical; if (horizontal)
option.state |= QStyle::State_Horizontal; // getNativeControlRegion usually gets ImplControlValue as 'val' (i.e. not the proper // subclass), so use random sensible values (doesn't matter anyway, as the wanted // geometry here depends only on button sizes)
option.maximum = 10;
option.minimum = 0;
option.sliderPosition = option.sliderValue = 4;
option.pageStep = 2; // Adjust coordinates to make the widget appear to be at (0,0), i.e. make // widget and screen coordinates the same. QStyle functions should use screen // coordinates but at least QPlastiqueStyle::subControlRect() is buggy // and sometimes uses widget coordinates.
option.rect = downscale(QRect({ 0, 0 }, contentRect.size()));
contentRect = upscale(
subControlRect(QStyle::CC_ScrollBar, &option, QStyle::SC_ScrollBarGroove));
contentRect.translate(boundingRect.left()
- (contentRect.width() - boundingRect.width()),
boundingRect.top());
boundingRect = contentRect;
retVal = true;
} break;
} case ControlType::TabItem:
{
QStyleOptionTab sot;
fillQStyleOptionTab(val, sot);
QSize aMinSize = upscale(sizeFromContents(QStyle::CT_TabBarTab, &sot,
downscale(contentRect.size(), Round::Ceil)),
Round::Ceil);
contentRect.setSize(aMinSize);
boundingRect = contentRect;
retVal = true; break;
} case ControlType::TabPane:
{ const TabPaneValue& rValue = static_cast<const TabPaneValue&>(val);
QStyleOptionTabWidgetFrame sotwf;
fullQStyleOptionTabWidgetFrame(sotwf, true);
QSize contentSize(
std::max(rValue.m_aTabHeaderRect.GetWidth(), controlRegion.GetWidth()),
rValue.m_aTabHeaderRect.GetHeight() + controlRegion.GetHeight());
QSize aMinSize = upscale(
sizeFromContents(QStyle::CT_TabWidget, &sotwf, downscale(contentSize, Round::Ceil)),
Round::Ceil);
contentRect.setSize(aMinSize);
boundingRect = contentRect;
retVal = true; break;
} default: break;
} if (retVal)
{
nativeBoundingRegion = toRectangle(boundingRect);
nativeContentRegion = toRectangle(contentRect);
}
return retVal;
}
/** Test whether the position is in the native widget. Ifthereturnvalueistrue,bIsInsidecontainsinformationwhether aPoswasorwasnotinsidethenativewidgetspecifiedbythe nType/nPartcombination.
*/ bool QtGraphics_Controls::hitTestNativeControl(ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion, const Point& rPos, bool& rIsInside)
{ if (nType == ControlType::Scrollbar)
{ if (nPart != ControlPart::ButtonUp && nPart != ControlPart::ButtonDown
&& nPart != ControlPart::ButtonLeft && nPart != ControlPart::ButtonRight)
{ // we adjust only for buttons (because some scrollbars have 3 buttons, // and LO core doesn't handle such scrollbars well) returnfalse;
}
rIsInside = false; bool bHorizontal = (nPart == ControlPart::ButtonLeft || nPart == ControlPart::ButtonRight);
QRect rect = toQRect(rControlRegion);
QPoint pos(rPos.X(), rPos.Y()); // Adjust coordinates to make the widget appear to be at (0,0), i.e. make // widget and screen coordinates the same. QStyle functions should use screen // coordinates but at least QPlastiqueStyle::subControlRect() is buggy // and sometimes uses widget coordinates.
pos -= rect.topLeft();
rect.moveTo(0, 0);
QStyleOptionSlider options;
options.orientation = bHorizontal ? Qt::Horizontal : Qt::Vertical; if (bHorizontal)
options.state |= QStyle::State_Horizontal;
options.rect = rect; // some random sensible values, since we call this code only for scrollbar buttons, // the slider position does not exactly matter
options.maximum = 10;
options.minimum = 0;
options.sliderPosition = options.sliderValue = 4;
options.pageStep = 2;
QStyle::SubControl control
= QApplication::style()->hitTestComplexControl(QStyle::CC_ScrollBar, &options, pos); if (nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonLeft)
rIsInside = (control == QStyle::SC_ScrollBarSubLine); else// DOWN, RIGHT
rIsInside = (control == QStyle::SC_ScrollBarAddLine); returntrue;
} returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.