diff --git a/src/declarativeimports/core/framesvgitem.cpp b/src/declarativeimports/core/framesvgitem.cpp index 342a37a8a..81f9e6376 100644 --- a/src/declarativeimports/core/framesvgitem.cpp +++ b/src/declarativeimports/core/framesvgitem.cpp @@ -1,546 +1,579 @@ /* * Copyright 2010 Marco Martin * Copyright 2014 David Edmundson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "framesvgitem.h" #include #include #include #include #include #include #include #include #include #include //floor() namespace Plasma { Q_GLOBAL_STATIC(ImageTexturesCache, s_cache) class FrameNode : public QSGNode { public: FrameNode(const QString& prefix, FrameSvg* svg) : QSGNode() , leftWidth(0) , rightWidth(0) , topHeight(0) , bottomHeight(0) { if (svg->enabledBorders() & FrameSvg::LeftBorder) leftWidth = svg->elementSize(prefix % QLatin1String("left")).width(); if (svg->enabledBorders() & FrameSvg::RightBorder) rightWidth = svg->elementSize(prefix % QLatin1String("right")).width(); if (svg->enabledBorders() & FrameSvg::TopBorder) topHeight = svg->elementSize(prefix % QLatin1String("top")).height(); if (svg->enabledBorders() & FrameSvg::BottomBorder) bottomHeight = svg->elementSize(prefix % QLatin1String("bottom")).height(); } QRect contentsRect(const QSize& size) const { const QSize contentSize(size.width() - leftWidth - rightWidth, size.height() - topHeight - bottomHeight); return QRect(QPoint(leftWidth, topHeight), contentSize); } private: int leftWidth; int rightWidth; int topHeight; int bottomHeight; }; class FrameItemNode : public ManagedTextureNode { public: enum FitMode { //render SVG at native resolution then stretch it in openGL FastStretch, //on resize re-render the part of the frame from the SVG Stretch, Tile }; FrameItemNode(FrameSvgItem* frameSvg, FrameSvg::EnabledBorders borders, FitMode fitMode, QSGNode* parent) : ManagedTextureNode() , m_frameSvg(frameSvg) , m_border(borders) , m_lastParent(parent) , m_fitMode(fitMode) { m_lastParent->appendChildNode(this); if (m_fitMode == Tile) { if (m_border == FrameSvg::TopBorder || m_border == FrameSvg::BottomBorder || m_border == FrameSvg::NoBorder) { static_cast(material())->setHorizontalWrapMode(QSGTexture::Repeat); static_cast(opaqueMaterial())->setHorizontalWrapMode(QSGTexture::Repeat); } if (m_border == FrameSvg::LeftBorder || m_border == FrameSvg::RightBorder || m_border == FrameSvg::NoBorder) { static_cast(material())->setVerticalWrapMode(QSGTexture::Repeat); static_cast(opaqueMaterial())->setVerticalWrapMode(QSGTexture::Repeat); } } if (m_fitMode == Tile || m_fitMode == FastStretch) { QString elementId = m_frameSvg->frameSvg()->actualPrefix() + FrameSvgHelpers::borderToElementId(m_border); m_elementNativeSize = m_frameSvg->frameSvg()->elementSize(elementId); if (m_elementNativeSize.isEmpty()) { //if the default element is empty, we can avoid the slower tiling path //this also avoids a divide by 0 error m_fitMode = FastStretch; } updateTexture(m_elementNativeSize, elementId); } } void updateTexture(const QSize &size, const QString &elementId) { QQuickWindow::CreateTextureOptions options; //Qt < 5.3.2. has a crash on some atlas textures #if (QT_VERSION > QT_VERSION_CHECK(5, 3, 2)) if (m_fitMode != Tile) { options = QQuickWindow::TextureCanUseAtlas; } #endif setTexture(s_cache->loadTexture(m_frameSvg->window(), m_frameSvg->frameSvg()->image(size, elementId), options)); } void reposition(const QRect& frameGeometry, QSize& fullSize) { QRect nodeRect = FrameSvgHelpers::sectionRect(m_border, frameGeometry, fullSize); //ensure we're not passing a weird rectangle to updateTexturedRectGeometry if(!nodeRect.isValid() || nodeRect.isEmpty()) nodeRect = QRect(); //the position of the relevant texture within this texture ID. //for atlas' this will only be a small part of the texture QRectF textureRect; if (m_fitMode == Tile) { textureRect = QRectF(0,0,1,1); //we can never be in an atlas for tiled images. //if tiling horizontally if (m_border == FrameSvg::TopBorder || m_border == FrameSvg::BottomBorder || m_border == FrameSvg::NoBorder) { textureRect.setWidth(nodeRect.width() / m_elementNativeSize.width()); } //if tiling vertically if (m_border == FrameSvg::LeftBorder || m_border == FrameSvg::RightBorder || m_border == FrameSvg::NoBorder) { textureRect.setHeight(nodeRect.height() / m_elementNativeSize.height()); } } else if (m_fitMode == Stretch) { QString prefix = m_frameSvg->frameSvg()->actualPrefix(); QString elementId = prefix + FrameSvgHelpers::borderToElementId(m_border); //re-render the SVG at new size updateTexture(nodeRect.size(), elementId); textureRect = texture()->normalizedTextureSubRect(); } else if (texture()) { // for fast stretch. textureRect = texture()->normalizedTextureSubRect(); } QSGGeometry::updateTexturedRectGeometry(geometry(), nodeRect, textureRect); markDirty(QSGNode::DirtyGeometry); } private: FrameSvgItem* m_frameSvg; FrameSvg::EnabledBorders m_border; QSGNode *m_lastParent; QSize m_elementNativeSize; FitMode m_fitMode; }; FrameSvgItemMargins::FrameSvgItemMargins(Plasma::FrameSvg *frameSvg, QObject *parent) : QObject(parent), m_frameSvg(frameSvg), m_fixed(false) { //qDebug() << "margins at: " << left() << top() << right() << bottom(); } qreal FrameSvgItemMargins::left() const { if (m_fixed) { return m_frameSvg->fixedMarginSize(Types::LeftMargin); } else { return m_frameSvg->marginSize(Types::LeftMargin); } } qreal FrameSvgItemMargins::top() const { if (m_fixed) { return m_frameSvg->fixedMarginSize(Types::TopMargin); } else { return m_frameSvg->marginSize(Types::TopMargin); } } qreal FrameSvgItemMargins::right() const { if (m_fixed) { return m_frameSvg->fixedMarginSize(Types::RightMargin); } else { return m_frameSvg->marginSize(Types::RightMargin); } } qreal FrameSvgItemMargins::bottom() const { if (m_fixed) { return m_frameSvg->fixedMarginSize(Types::BottomMargin); } else { return m_frameSvg->marginSize(Types::BottomMargin); } } qreal FrameSvgItemMargins::horizontal() const { return left() + right(); } qreal FrameSvgItemMargins::vertical() const { return top() + bottom(); } void FrameSvgItemMargins::update() { emit marginsChanged(); } void FrameSvgItemMargins::setFixed(bool fixed) { if (fixed == m_fixed) { return; } m_fixed = fixed; emit marginsChanged(); } bool FrameSvgItemMargins::isFixed() const { return m_fixed; } FrameSvgItem::FrameSvgItem(QQuickItem *parent) : QQuickItem(parent), m_textureChanged(false), m_sizeChanged(false), m_fastPath(true) { m_frameSvg = new Plasma::FrameSvg(this); m_margins = new FrameSvgItemMargins(m_frameSvg, this); m_fixedMargins = new FrameSvgItemMargins(m_frameSvg, this); m_fixedMargins->setFixed(true); setFlag(ItemHasContents, true); connect(m_frameSvg, SIGNAL(repaintNeeded()), this, SLOT(doUpdate())); connect(&Units::instance(), &Units::devicePixelRatioChanged, this, &FrameSvgItem::updateDevicePixelRatio); connect(m_frameSvg, &Svg::fromCurrentThemeChanged, this, &FrameSvgItem::fromCurrentThemeChanged); connect(m_frameSvg, &Svg::statusChanged, this, &FrameSvgItem::statusChanged); } FrameSvgItem::~FrameSvgItem() { } void FrameSvgItem::setImagePath(const QString &path) { if (m_frameSvg->imagePath() == path) { return; } updateDevicePixelRatio(); m_frameSvg->setImagePath(path); - m_frameSvg->setElementPrefix(m_prefix); if (implicitWidth() <= 0) { setImplicitWidth(m_frameSvg->marginSize(Plasma::Types::LeftMargin) + m_frameSvg->marginSize(Plasma::Types::RightMargin)); } if (implicitHeight() <= 0) { setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin)); } emit imagePathChanged(); m_margins->update(); m_fixedMargins->update(); if (isComponentComplete()) { + applyPrefixes(); + m_frameSvg->resizeFrame(QSizeF(width(), height())); m_textureChanged = true; update(); } } QString FrameSvgItem::imagePath() const { return m_frameSvg->imagePath(); } -void FrameSvgItem::setPrefix(const QString &prefix) +void FrameSvgItem::setPrefix(const QVariant &prefixes) { - if (m_prefix == prefix) { + QStringList prefixList; + //is this a simple string? + if (prefixes.canConvert()) { + prefixList << prefixes.toString(); + } else if (prefixes.canConvert()) { + prefixList = prefixes.toStringList(); + } + + if (m_prefixes == prefixList) { return; } - m_frameSvg->setElementPrefix(prefix); - m_prefix = prefix; + m_prefixes = prefixList; + applyPrefixes(); if (implicitWidth() <= 0) { setImplicitWidth(m_frameSvg->marginSize(Plasma::Types::LeftMargin) + m_frameSvg->marginSize(Plasma::Types::RightMargin)); } if (implicitHeight() <= 0) { setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin)); } emit prefixChanged(); m_margins->update(); m_fixedMargins->update(); if (isComponentComplete()) { m_frameSvg->resizeFrame(QSizeF(width(), height())); m_textureChanged = true; update(); } } -QString FrameSvgItem::prefix() const +QVariant FrameSvgItem::prefix() const { - return m_prefix; + return m_prefixes; } FrameSvgItemMargins *FrameSvgItem::margins() const { return m_margins; } FrameSvgItemMargins *FrameSvgItem::fixedMargins() const { return m_fixedMargins; } void FrameSvgItem::setColorGroup(Plasma::Theme::ColorGroup group) { if (m_frameSvg->colorGroup() == group) { return; } m_frameSvg->setColorGroup(group); emit colorGroupChanged(); } Plasma::Theme::ColorGroup FrameSvgItem::colorGroup() const { return m_frameSvg->colorGroup(); } bool FrameSvgItem::fromCurrentTheme() const { return m_frameSvg->fromCurrentTheme(); } void FrameSvgItem::setStatus(Plasma::Svg::Status status) { m_frameSvg->setStatus(status); } Plasma::Svg::Status FrameSvgItem::status() const { return m_frameSvg->status(); } void FrameSvgItem::setEnabledBorders(const Plasma::FrameSvg::EnabledBorders borders) { if (m_frameSvg->enabledBorders() == borders) { return; } m_frameSvg->setEnabledBorders(borders); emit enabledBordersChanged(); m_textureChanged = true; m_margins->update(); update(); } Plasma::FrameSvg::EnabledBorders FrameSvgItem::enabledBorders() const { return m_frameSvg->enabledBorders(); } bool FrameSvgItem::hasElementPrefix(const QString &prefix) const { return m_frameSvg->hasElementPrefix(prefix); } void FrameSvgItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { if (isComponentComplete()) { m_frameSvg->resizeFrame(newGeometry.size()); m_sizeChanged = true; } QQuickItem::geometryChanged(newGeometry, oldGeometry); } void FrameSvgItem::doUpdate() { + //if the theme changed, the available prefix may have changed as well + applyPrefixes(); + if (implicitWidth() <= 0) { setImplicitWidth(m_frameSvg->marginSize(Plasma::Types::LeftMargin) + m_frameSvg->marginSize(Plasma::Types::RightMargin)); } if (implicitHeight() <= 0) { setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin)); } QString prefix = m_frameSvg->actualPrefix(); bool hasOverlay = !prefix.startsWith(QLatin1String("mask-")) && m_frameSvg->hasElement(prefix % QLatin1String("overlay")); bool hasComposeOverBorder = m_frameSvg->hasElement(prefix % QLatin1String("hint-compose-over-border")) && m_frameSvg->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center")); m_fastPath = !hasOverlay && !hasComposeOverBorder; m_textureChanged = true; update(); m_margins->update(); m_fixedMargins->update(); emit repaintNeeded(); } Plasma::FrameSvg *FrameSvgItem::frameSvg() const { return m_frameSvg; } QSGNode *FrameSvgItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) { if (!window() || !m_frameSvg || - (!m_frameSvg->hasElementPrefix(m_frameSvg->actualPrefix()) && !m_frameSvg->hasElementPrefix(m_prefix))) { + (!m_frameSvg->hasElementPrefix(m_frameSvg->actualPrefix()) && !m_frameSvg->hasElementPrefix(m_frameSvg->prefix()))) { delete oldNode; return nullptr; } if (m_fastPath) { if (m_textureChanged) { delete oldNode; oldNode = 0; } if (!oldNode) { QString prefix = m_frameSvg->actualPrefix(); oldNode = new FrameNode(prefix, m_frameSvg); bool tileCenter = (m_frameSvg->hasElement(QStringLiteral("hint-tile-center")) || m_frameSvg->hasElement(prefix % QLatin1String("hint-tile-center"))); bool stretchBorders = (m_frameSvg->hasElement(QStringLiteral("hint-stretch-borders")) || m_frameSvg->hasElement(prefix % QLatin1String("hint-stretch-borders"))); FrameItemNode::FitMode borderFitMode = stretchBorders ? FrameItemNode::Stretch : FrameItemNode::Tile; FrameItemNode::FitMode centerFitMode = tileCenter ? FrameItemNode::Tile: FrameItemNode::Stretch; new FrameItemNode(this, FrameSvg::NoBorder, centerFitMode, oldNode); new FrameItemNode(this, FrameSvg::TopBorder | FrameSvg::LeftBorder, FrameItemNode::FastStretch, oldNode); new FrameItemNode(this, FrameSvg::TopBorder | FrameSvg::RightBorder, FrameItemNode::FastStretch, oldNode); new FrameItemNode(this, FrameSvg::TopBorder, borderFitMode, oldNode); new FrameItemNode(this, FrameSvg::BottomBorder, borderFitMode, oldNode); new FrameItemNode(this, FrameSvg::BottomBorder | FrameSvg::LeftBorder, FrameItemNode::FastStretch, oldNode); new FrameItemNode(this, FrameSvg::BottomBorder | FrameSvg::RightBorder, FrameItemNode::FastStretch, oldNode); new FrameItemNode(this, FrameSvg::LeftBorder, borderFitMode, oldNode); new FrameItemNode(this, FrameSvg::RightBorder, borderFitMode, oldNode); m_sizeChanged = true; m_textureChanged = false; } if (m_sizeChanged) { FrameNode* frameNode = static_cast(oldNode); QSize frameSize(width(), height()); QRect geometry = frameNode->contentsRect(frameSize); for(int i = 0; ichildCount(); ++i) { FrameItemNode* it = static_cast(oldNode->childAtIndex(i)); it->reposition(geometry, frameSize); } m_sizeChanged = false; } } else { ManagedTextureNode *textureNode = dynamic_cast(oldNode); if (!textureNode) { delete oldNode; textureNode = new ManagedTextureNode; textureNode->setFiltering(QSGTexture::Nearest); m_textureChanged = true; //force updating the texture on our newly created node oldNode = textureNode; } if ((m_textureChanged || m_sizeChanged) || textureNode->texture()->textureSize() != m_frameSvg->size()) { QImage image = m_frameSvg->framePixmap().toImage(); textureNode->setTexture(s_cache->loadTexture(window(), image)); textureNode->setRect(0, 0, width(), height()); m_textureChanged = false; m_sizeChanged = false; } } return oldNode; } void FrameSvgItem::classBegin() { QQuickItem::classBegin(); m_frameSvg->setRepaintBlocked(true); } void FrameSvgItem::componentComplete() { QQuickItem::componentComplete(); m_frameSvg->resizeFrame(QSize(width(), height())); m_frameSvg->setRepaintBlocked(false); m_textureChanged = true; } void FrameSvgItem::updateDevicePixelRatio() { //devicepixelratio is always set integer in the svg, so needs at least 192dpi to double up. //(it needs to be integer to have lines contained inside a svg piece to keep being pixel aligned) if (window()) { m_frameSvg->setDevicePixelRatio(qMax(1.0, floor(window()->devicePixelRatio()))); } else { m_frameSvg->setDevicePixelRatio(qMax(1.0, floor(qApp->devicePixelRatio()))); } m_frameSvg->setScaleFactor(qMax(1.0, floor(Units::instance().devicePixelRatio()))); m_textureChanged = true; } +void FrameSvgItem::applyPrefixes() +{ + if (m_prefixes.isEmpty() || m_frameSvg->imagePath().isEmpty()) { + return; + } + + bool found = false; + for (const QString &prefix : m_prefixes) { + if (m_frameSvg->hasElementPrefix(prefix)) { + m_frameSvg->setElementPrefix(prefix); + found = true; + break; + } + } + if (!found) { + qWarning() << "The image" << m_frameSvg->imagePath() << "doesn't contain any of the prefixes" << m_prefixes; + //this setElementPrefix is done to keep the same behavior as before, when it was a simple string + m_frameSvg->setElementPrefix(m_prefixes.last()); + } +} + } // Plasma namespace diff --git a/src/declarativeimports/core/framesvgitem.h b/src/declarativeimports/core/framesvgitem.h index b32432364..f7203acc2 100644 --- a/src/declarativeimports/core/framesvgitem.h +++ b/src/declarativeimports/core/framesvgitem.h @@ -1,248 +1,254 @@ /*************************************************************************** * Copyright 2010 Marco Martin * * Copyright 2014 David Edmundson * * * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef FRAMESVGITEM_P #define FRAMESVGITEM_P #include #include #include #include "units.h" namespace Plasma { class FrameSvg; /** * @class FrameSvgItemMargins * * @short The sizes of a frame's margins. */ class FrameSvgItemMargins : public QObject { Q_OBJECT /** * Width in pixels of the left margin. */ Q_PROPERTY(qreal left READ left NOTIFY marginsChanged) /** * Height in pixels of the top margin. */ Q_PROPERTY(qreal top READ top NOTIFY marginsChanged) /** * Width in pixels of the right margin. */ Q_PROPERTY(qreal right READ right NOTIFY marginsChanged) /** * Height in pixels of the bottom margin. */ Q_PROPERTY(qreal bottom READ bottom NOTIFY marginsChanged) /** * Width in pixels of the left and right margins combined. */ Q_PROPERTY(qreal horizontal READ horizontal NOTIFY marginsChanged) /** * Height in pixels of the top and bottom margins combined. */ Q_PROPERTY(qreal vertical READ vertical NOTIFY marginsChanged) public: FrameSvgItemMargins(Plasma::FrameSvg *frameSvg, QObject *parent = 0); qreal left() const; qreal top() const; qreal right() const; qreal bottom() const; qreal horizontal() const; qreal vertical() const; void setFixed(bool fixed); bool isFixed() const; public Q_SLOTS: void update(); Q_SIGNALS: void marginsChanged(); private: FrameSvg *m_frameSvg; bool m_fixed; }; /** * @class FrameSvgItem * * @short Provides an SVG with borders. * * It is exposed as org.kde.plasma.core.FrameSvgItem */ class FrameSvgItem : public QQuickItem { Q_OBJECT Q_INTERFACES(QQmlParserStatus) /** * Theme relative path of the svg, like "widgets/background" */ Q_PROPERTY(QString imagePath READ imagePath WRITE setImagePath NOTIFY imagePathChanged) /** * prefix for the 9 piece svg, like "pushed" or "normal" for the button * see http://techbase.kde.org/Development/Tutorials/Plasma/ThemeDetails * for a list of paths and prefixes + * It can also be an array of strings, specifying a fallback chain in case + * the first element isn't found in the theme, eg ["toolbutton-normal", "normal"] + * so it's easy to keep backwards compatibility with old themes + * (Note: fallback chain is supported only @since 5.32) */ - Q_PROPERTY(QString prefix READ prefix WRITE setPrefix NOTIFY prefixChanged) + Q_PROPERTY(QVariant prefix READ prefix WRITE setPrefix NOTIFY prefixChanged) /** * The margins of the frame, read only * @see FrameSvgItemMargins */ Q_PROPERTY(QObject *margins READ margins CONSTANT) /** * The margins of the frame, regardless if they are enabled or not * read only * @see FrameSvgItemMargins */ Q_PROPERTY(QObject *fixedMargins READ fixedMargins CONSTANT) /** * The borders that will be rendered, it's a flag combination of: * NoBorder * TopBorder * BottomBorder * LeftBorder * RightBorder */ Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders WRITE setEnabledBorders NOTIFY enabledBordersChanged) /** * Holds whether the current svg is present in the current theme and NO fallback is involved */ Q_PROPERTY(bool fromCurrentTheme READ fromCurrentTheme NOTIFY fromCurrentThemeChanged) /** * Set a color group for the FrameSvgItem. * if the Svg uses stylesheets and has elements * that are eithe TextColor or BackgroundColor class, * make them use ButtonTextColor/ButtonBackgroundColor * or ViewTextColor/ViewBackgroundColor, ComplementaryTextColor etc. */ Q_PROPERTY(Plasma::Theme::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY colorGroupChanged) /** * Sets the image in a selected status. * Svgs can be colored with system color themes, if the status is selected, * the TextColor will become HighlightedText color and BackgroundColor * will become HighlightColor, making the svg graphics (for instance an icon) * will look correct together selected text * @see Plasma::Svg::status * @since 5.23 */ Q_PROPERTY(Plasma::Svg::Status status READ status WRITE setStatus NOTIFY statusChanged) public: /** * @return true if the svg has the necessary elements with the given prefix * to draw a frame * @param prefix the given prefix we want to check if drawable */ Q_INVOKABLE bool hasElementPrefix(const QString &prefix) const; /// @cond INTERNAL_DOCS FrameSvgItem(QQuickItem *parent = 0); ~FrameSvgItem(); void setImagePath(const QString &path); QString imagePath() const; - void setPrefix(const QString &prefix); - QString prefix() const; + void setPrefix(const QVariant &prefix); + QVariant prefix() const; void setEnabledBorders(const Plasma::FrameSvg::EnabledBorders borders); Plasma::FrameSvg::EnabledBorders enabledBorders() const; FrameSvgItemMargins *margins() const; FrameSvgItemMargins *fixedMargins() const; void setColorGroup(Plasma::Theme::ColorGroup group); Plasma::Theme::ColorGroup colorGroup() const; bool fromCurrentTheme() const; void setStatus(Plasma::Svg::Status status); Plasma::Svg::Status status() const; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; /** * Only to be used from inside this library, is not intended to be invokable */ Plasma::FrameSvg *frameSvg() const; QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; protected: void classBegin() Q_DECL_OVERRIDE; void componentComplete() Q_DECL_OVERRIDE; /// @endcond Q_SIGNALS: void imagePathChanged(); void prefixChanged(); void enabledBordersChanged(); void fromCurrentThemeChanged(); void colorGroupChanged(); void repaintNeeded(); void statusChanged(); private Q_SLOTS: void doUpdate(); void updateDevicePixelRatio(); private: + void applyPrefixes(); + Plasma::FrameSvg *m_frameSvg; FrameSvgItemMargins *m_margins; FrameSvgItemMargins *m_fixedMargins; - QString m_prefix; + QStringList m_prefixes; bool m_textureChanged; bool m_sizeChanged; bool m_fastPath; }; } #endif diff --git a/src/declarativeimports/plasmastyle/ToolButtonStyle.qml b/src/declarativeimports/plasmastyle/ToolButtonStyle.qml index 06a7a2298..192aa5b52 100644 --- a/src/declarativeimports/plasmastyle/ToolButtonStyle.qml +++ b/src/declarativeimports/plasmastyle/ToolButtonStyle.qml @@ -1,410 +1,410 @@ /* * Copyright 2014 by Marco Martin * Copyright 2014 by David Edmundson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls.Styles 1.1 as QtQuickControlStyle import QtQuick.Layouts 1.1 import QtQuick.Controls.Private 1.0 as QtQuickControlsPrivate import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.plasma.components 2.0 as PlasmaComponents import "private" as Private QtQuickControlStyle.ButtonStyle { id: style property int minimumWidth property int minimumHeight property bool flat: control.flat !== undefined ? control.flat : !(control.checkable && control.checked) property bool controlHovered: control.hovered && !(QtQuickControlsPrivate.Settings.hasTouchScreen && QtQuickControlsPrivate.Settings.isMobile) label: Item { //wrapper is needed as we are adjusting the preferredHeight of the layout from the default //and the implicitHeight is implicitly read only implicitHeight: buttonContent.Layout.preferredHeight implicitWidth: buttonContent.implicitWidth RowLayout { id: buttonContent anchors.fill: parent spacing: units.smallSpacing Layout.preferredHeight: Math.max(units.iconSizes.small, label.implicitHeight) property real minimumWidth: Layout.minimumWidth + style.padding.left + style.padding.right onMinimumWidthChanged: { if (control.minimumWidth !== undefined) { style.minimumWidth = minimumWidth; control.minimumWidth = minimumWidth; } } property real minimumHeight: Layout.preferredHeight + style.padding.top + style.padding.bottom onMinimumHeightChanged: { if (control.minimumHeight !== undefined) { style.minimumHeight = minimumHeight; control.minimumHeight = minimumHeight; } } PlasmaCore.IconItem { id: icon source: control.iconName || control.iconSource anchors.verticalCenter: parent.verticalCenter implicitHeight: label.implicitHeight implicitWidth: implicitHeight Layout.minimumWidth: valid ? parent.height: 0 Layout.maximumWidth: Layout.minimumWidth visible: valid Layout.minimumHeight: Layout.minimumWidth Layout.maximumHeight: Layout.minimumWidth Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter active: style.controlHovered colorGroup: controlHovered || !flat ? PlasmaCore.Theme.ButtonColorGroup : PlasmaCore.ColorScope.colorGroup } PlasmaComponents.Label { id: label anchors.verticalCenter: parent.verticalCenter Layout.minimumWidth: implicitWidth text: QtQuickControlsPrivate.StyleHelpers.stylizeMnemonics(control.text) font: control.font || theme.defaultFont visible: control.text != "" Layout.fillWidth: true color: controlHovered || !flat ? theme.buttonTextColor : PlasmaCore.ColorScope.textColor horizontalAlignment: icon.valid ? Text.AlignLeft : Text.AlignHCenter elide: Text.ElideRight } PlasmaExtras.ConditionalLoader { id: arrow when: control.menu !== null visible: when Layout.minimumWidth: units.iconSizes.small Layout.maximumWidth: Layout.minimumWidth Layout.minimumHeight: Layout.minimumWidth Layout.maximumHeight: Layout.maximumWidth Layout.alignment: Qt.AlignVCenter source: Component { PlasmaCore.SvgItem { visible: control.menu !== null svg: PlasmaCore.Svg { imagePath: "widgets/arrows" colorGroup: style.controlHovered || !style.flat ? PlasmaCore.Theme.ButtonColorGroup : PlasmaCore.ColorScope.colorGroup } elementId: "down-arrow" } } } } } background: { if (control.text.length == 0 && (control.parent && (control.parent.spacing === undefined || control.parent.spacing !== 0)) && !style.flat && !control.menu) { return roundButtonComponent } else { return buttonComponent } } Component { id: roundButtonComponent Item { id: roundButtonDelegate implicitHeight: Math.floor(theme.mSize(theme.defaultFont).height*1.6) implicitWidth: implicitHeight property QtObject margins: QtObject { property int left: control.width/8 property int top: control.width/8 property int right: control.width/8 property int bottom: control.width/8 } property alias hasOverState: roundShadow.hasOverState Private.RoundShadow { id: roundShadow visible: !style.flat || control.activeFocus anchors.fill: parent state: { if (control.pressed) { return "hidden" } else if (style.controlHovered) { return "hover" } else if (control.activeFocus) { return "focus" } else { return "shadow" } } } PlasmaCore.Svg { id: buttonSvg imagePath: "widgets/actionbutton" } PlasmaCore.SvgItem { id: buttonItem svg: buttonSvg elementId: (control.pressed || control.checked) ? "pressed" : "normal" width: Math.floor(parent.height/2) * 2 height: width anchors.centerIn: parent //internal: if there is no hover status, don't paint on mouse over in touchscreens opacity: (control.pressed || control.checked || !style.flat || (roundShadow.hasOverState && style.controlHovered)) ? 1 : 0 Behavior on opacity { PropertyAnimation { duration: units.longDuration } } } } } Component { id: buttonComponent Item { id: buttonSurface implicitHeight: Math.floor(theme.mSize(theme.defaultFont).height*1.6) implicitWidth: { if (control.text.length == 0) { implicitHeight; } else { Math.floor(theme.mSize(theme.defaultFont).width*12); } } Connections { target: control onHoveredChanged: { if (style.controlHovered) { control.z += 2 } else { control.z -= 2 } } } Private.ButtonShadow { id: shadow visible: !style.flat || control.activeFocus anchors.fill: parent enabledBorders: surfaceNormal.enabledBorders state: { if (control.pressed) { return "hidden" } else if (style.controlHovered) { return "hover" } else if (control.activeFocus) { return "focus" } else { return "shadow" } } } //This code is duplicated here and Button and ToolButton //maybe we can make an AbstractButton class? PlasmaCore.FrameSvgItem { id: surfaceNormal anchors.fill: parent imagePath: "widgets/button" - prefix: "normal" + prefix: style.flat ? ["toolbutton-hover", "normal"] : "normal" enabledBorders: { if (style.flat || !control.parent || control.parent.width < control.parent.implicitWidth || control.parent.spacing !== 0 || !bordersSvg.hasElement("pressed-hint-compose-over-border")) { if (shadows !== null) { shadows.destroy() } return "AllBorders" } var borders = new Array() if (control.x == 0) { borders.push("LeftBorder") shadow.anchors.leftMargin = 0; } else { shadow.anchors.leftMargin = -1; } if (control.y == 0) { borders.push("TopBorder") shadow.anchors.topMargin = 0; } else { shadow.anchors.topMargin = -1; } if (control.x + control.width >= control.parent.width) { borders.push("RightBorder") shadow.anchors.rightMargin = 0; } else { shadow.anchors.rightMargin = -1; } if (control.y + control.height >= control.parent.height) { borders.push("BottomBorder") shadow.anchors.bottomMargin = 0; } else { shadow.anchors.bottomMargin = -1; } if (shadows === null) { shadows = shadowsComponent.createObject(buttonSurface) } return borders.join("|") } PlasmaCore.Svg { id: bordersSvg imagePath: "widgets/button" } } PlasmaCore.FrameSvgItem { id: surfacePressed anchors.fill: parent imagePath: "widgets/button" - prefix: "pressed" + prefix: style.flat ? ["toolbutton-pressed", "pressed"] : "pressed" enabledBorders: surfaceNormal.enabledBorders opacity: 0 } property Item shadows Component { id: shadowsComponent Item { anchors.fill: parent PlasmaCore.SvgItem { svg: bordersSvg width: naturalSize.width elementId: (buttonSurface.state == "pressed" ? surfacePressed.prefix : surfaceNormal.prefix) + "-left" visible: button.x > 0 anchors { left: parent.left top: parent.top bottom: parent.bottom margins: 1 leftMargin: -1 } } PlasmaCore.SvgItem { svg: bordersSvg width: naturalSize.width elementId: (buttonSurface.state == "pressed" ? surfacePressed.prefix : surfaceNormal.prefix) + "-right" visible: button.x + button.width < button.parent.width anchors { right: parent.right top: parent.top bottom: parent.bottom margins: 1 rightMargin: -1 } } PlasmaCore.SvgItem { svg: bordersSvg height: naturalSize.height elementId: (buttonSurface.state == "pressed" ? surfacePressed.prefix : surfaceNormal.prefix) + "-top" visible: button.y > 0 anchors { left: parent.left top: parent.top right: parent.right margins: 1 topMargin: -1 } } PlasmaCore.SvgItem { svg: bordersSvg height: naturalSize.height elementId: (buttonSurface.state == "pressed" ? surfacePressed.prefix : surfaceNormal.prefix) + "-bottom" visible: button.y + button.height < button.parent.height anchors { left: parent.left right: parent.right bottom: parent.bottom margins: 1 bottomMargin: -1 } } } } state: (control.pressed || control.checked ? "pressed" : (style.controlHovered ? "hover" : "normal")) states: [ State { name: "normal" PropertyChanges { target: surfaceNormal opacity: style.flat ? 0 : 1 } PropertyChanges { target: surfacePressed opacity: 0 } }, State { name: "hover" PropertyChanges { target: surfaceNormal opacity: 1 } PropertyChanges { target: surfacePressed opacity: 0 } }, State { name: "pressed" PropertyChanges { target: surfaceNormal opacity: 0 } PropertyChanges { target: surfacePressed opacity: 1 } } ] transitions: [ Transition { //Cross fade from pressed to normal ParallelAnimation { NumberAnimation { target: surfaceNormal; property: "opacity"; duration: 100 } NumberAnimation { target: surfacePressed; property: "opacity"; duration: 100 } } } ] Component.onCompleted: { padding.top = surfaceNormal.margins.top padding.left = surfaceNormal.margins.left padding.right = surfaceNormal.margins.right padding.bottom = surfaceNormal.margins.bottom } } } } diff --git a/src/desktoptheme/breeze/widgets/button.svgz b/src/desktoptheme/breeze/widgets/button.svgz index 61a23f513..182491163 100644 Binary files a/src/desktoptheme/breeze/widgets/button.svgz and b/src/desktoptheme/breeze/widgets/button.svgz differ