Merge branch 'davidedmundson/framesvg_native'

Use FrameSVG as 9 tiles instead of uploading a big texture of the finished frame each time.
This also saves the cache being populated with full created frames in different sizes; which end up taking up space in the disk and shared memory cache as well as the GPU memory.
A code path falls back to the original uploading the entire texture if obscure settings are used, i.e overlay.

Benchmarks:
apitrace when resizing a frame goes from an average of 7.6ms per frame of CPU time just for the swizzling and uploading to 1.4ms
GPU time also drops from 40us to 10us

Themes will need to remove stretch-borders (when we gain nothing from stretching; i.e Breeze) to get the most out of it.

REVIEW: 119330
This commit is contained in:
Aleix Pol 2014-07-21 18:38:11 +02:00
commit ab93d83890
13 changed files with 527 additions and 82 deletions

View File

@ -64,7 +64,7 @@ PlasmaComponents.Page {
onClicked: {
console.log("Clicked");
pageStack.push(Qt.createComponent(plasmoid.file("ui", "Scrollers.qml")))
pageStack.push(Qt.createComponent("Scrollers.qml"))
}
Keys.onTabPressed: bt2.forceActiveFocus();

View File

@ -69,7 +69,7 @@ Page {
text: title
}
}
onClicked: pageStack.push(Qt.createComponent(plasmoid.file("ui", page)))
onClicked: pageStack.push(Qt.createComponent(page))
}
}
ScrollBar {

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2011 by Daker Fernandes Pinheiro <dakerfp@gmail.com>
*
* 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.
*/
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0
import org.kde.plasma.components 2.0
FrameSvgItem {
width: 300
height: 400
imagePath: "widgets/background"
ToolBar {
id: toolBar
z: 10
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 10
}
}
PageStack {
id: pageStack
toolBar: toolBar
clip: true
anchors {
top: toolBar.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
margins: 10
}
initialPage: Qt.createComponent("Menu.qml")
}
}

View File

@ -22,7 +22,13 @@
#include <QQuickWindow>
#include <QSGTexture>
#include <QSGGeometry>
#include <QDebug>
#include <QPainter>
#include <plasma/private/framesvg_p.h>
#include <plasma/private/framesvg_helpers.h>
#include "svgtexturenode.h"
@ -31,6 +37,109 @@
namespace Plasma
{
class FrameItemNode : public SVGTextureNode
{
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)
: SVGTextureNode()
, 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<QSGTextureMaterial*>(material())->setHorizontalWrapMode(QSGTexture::Repeat);
static_cast<QSGOpaqueTextureMaterial*>(opaqueMaterial())->setHorizontalWrapMode(QSGTexture::Repeat);
}
if (m_border == FrameSvg::LeftBorder || m_border == FrameSvg::RightBorder || m_border == FrameSvg::NoBorder) {
static_cast<QSGTextureMaterial*>(material())->setVerticalWrapMode(QSGTexture::Repeat);
static_cast<QSGOpaqueTextureMaterial*>(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);
updateTexture(m_elementNativeSize, elementId, false);
}
}
void updateTexture(const QSize &size, const QString &elementId, bool composeOverBorder)
{
QImage image = m_frameSvg->frameSvg()->image(size, elementId);
QString prefix = m_frameSvg->frameSvg()->actualPrefix();
//in compose over border we paint the center over the full size
//then blend in an alpha mask generated from the corners to
//remove the garbage left in the corners
if (m_border == FrameSvg::NoBorder && m_fitMode == Stretch && composeOverBorder) {
QPixmap pixmap = QPixmap::fromImage(image);
QPainter p(&pixmap);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.drawPixmap(QRect(QPoint(0, 0), size), m_frameSvg->frameSvg()->alphaMask());
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
image = pixmap.toImage();
}
QSGTexture *texture = m_frameSvg->window()->createTextureFromImage(image);
setTexture(texture);
}
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();
QRectF textureRect = QRectF(0,0,1,1);
if (m_fitMode == Tile) {
if (m_border == FrameSvg::TopBorder || m_border == FrameSvg::BottomBorder || m_border == FrameSvg::NoBorder) {
textureRect.setWidth(nodeRect.width() / m_elementNativeSize.width());
}
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();
bool composeOverBorder = (m_border == FrameSvg::NoBorder) && (m_frameSvg->frameSvg()->hasElement(prefix % "hint-compose-over-border") &&
m_frameSvg->frameSvg()->hasElement("mask-" % prefix % "center"));
QString elementId = prefix + FrameSvgHelpers::borderToElementId(m_border);
if (composeOverBorder) {
nodeRect = QRect(QPoint(0,0), fullSize);
}
//re-render the SVG at new size
updateTexture(nodeRect.size(), elementId, composeOverBorder);
} // for fast stretch, we don't have to do anything
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),
@ -98,7 +207,9 @@ bool FrameSvgItemMargins::isFixed() const
FrameSvgItem::FrameSvgItem(QQuickItem *parent)
: QQuickItem(parent),
m_textureChanged(false)
m_textureChanged(false),
m_sizeChanged(false),
m_fastPath(true)
{
m_frameSvg = new Plasma::FrameSvg(this);
m_margins = new FrameSvgItemMargins(m_frameSvg, this);
@ -215,7 +326,7 @@ void FrameSvgItem::geometryChanged(const QRectF &newGeometry,
{
if (isComponentComplete()) {
m_frameSvg->resizeFrame(newGeometry.size());
m_textureChanged = true;
m_sizeChanged = true;
}
QQuickItem::geometryChanged(newGeometry, oldGeometry);
}
@ -230,6 +341,9 @@ void FrameSvgItem::doUpdate()
setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin));
}
QString prefix = m_frameSvg->actualPrefix();
bool hasOverlay = !prefix.startsWith(QStringLiteral("mask-")) && m_frameSvg->hasElement(prefix % "overlay");
m_fastPath = !hasOverlay;
m_textureChanged = true;
update();
}
@ -278,23 +392,68 @@ QSGNode *FrameSvgItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaint
return Q_NULLPTR;
}
SVGTextureNode *textureNode = static_cast<SVGTextureNode *>(oldNode);
if (!textureNode) {
textureNode = new SVGTextureNode;
textureNode->setFiltering(QSGTexture::Nearest);
m_textureChanged = true; //force updating the texture on our newly created node
if (m_fastPath) {
if (m_textureChanged) {
delete oldNode;
oldNode = 0;
}
if (!oldNode) {
oldNode = new QSGNode;
QString prefix = m_frameSvg->actualPrefix();
bool tileCenter = (m_frameSvg->hasElement("hint-tile-center") || m_frameSvg->hasElement(prefix % "hint-tile-center"));
bool stretchBorders = (m_frameSvg->hasElement("hint-stretch-borders") || m_frameSvg->hasElement(prefix % "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); //needs to be de first, in case of composeOverBorder
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) {
QSize frameSize(width(), height());
QRect geometry = m_frameSvg->contentsRect().toRect();
for(int i = 0; i<oldNode->childCount(); ++i) {
FrameItemNode* it = static_cast<FrameItemNode*>(oldNode->childAtIndex(i));
it->reposition(geometry, frameSize);
}
m_sizeChanged = false;
}
} else {
SVGTextureNode *textureNode = dynamic_cast<SVGTextureNode *>(oldNode);
if (!textureNode) {
delete oldNode;
textureNode = new SVGTextureNode;
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();
QSGTexture *texture = window()->createTextureFromImage(image);
texture->setFiltering(QSGTexture::Nearest);
textureNode->setTexture(texture);
textureNode->setRect(0, 0, width(), height());
m_textureChanged = false;
m_sizeChanged = false;
}
}
if (m_textureChanged || textureNode->texture()->textureSize() != m_frameSvg->size()) {
const QImage image = m_frameSvg->framePixmap().toImage();
QSGTexture *texture = window()->createTextureFromImage(image);
texture->setFiltering(QSGTexture::Nearest);
textureNode->setTexture(texture);
m_textureChanged = false;
textureNode->setRect(0, 0, width(), height());
}
return textureNode;
return oldNode;
}
void FrameSvgItem::componentComplete()

View File

@ -31,6 +31,7 @@ namespace Plasma
{
class FrameSvg;
class SVGTextureNode;
class FrameSvgItemMargins : public QObject
{
@ -188,6 +189,8 @@ private:
QString m_prefix;
Units m_units;
bool m_textureChanged;
bool m_sizeChanged;
bool m_fastPath;
};
}

View File

@ -137,6 +137,12 @@ QSGNode *SvgItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updateP
return Q_NULLPTR;
}
//this is more than just an optimisation, uploading a null image to QSGAtlasTexture causes a crash
if (width() == 0 || height() == 0) {
delete oldNode;
return Q_NULLPTR;
}
SVGTextureNode *textureNode = static_cast<SVGTextureNode *>(oldNode);
if (!textureNode) {
textureNode = new SVGTextureNode;
@ -152,7 +158,8 @@ QSGNode *SvgItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updateP
//setContainsMultipleImages has to be done there since m_frameSvg can be shared with somebody else
m_svg.data()->setContainsMultipleImages(!m_elementID.isEmpty());
const QImage image = m_svg.data()->image(QSize(width(), height()), m_elementID);
QSGTexture *texture = window()->createTextureFromImage(image);
QSGTexture *texture = window()->createTextureFromImage(image, QQuickWindow::TextureCanUseAtlas);
if (m_smooth) {
texture->setFiltering(QSGTexture::Linear);
}

View File

@ -24,8 +24,6 @@
#include <QDebug>
#include <QPropertyAnimation>
#include "framesvgitem.h"
#include <kdeclarative/qmlobject.h>
ToolTipDialog::ToolTipDialog(QQuickItem *parent)

View File

@ -32,8 +32,9 @@
#include <QDebug>
#include <theme.h>
#include <private/svg_p.h>
#include "theme.h"
#include "private/svg_p.h"
#include "private/framesvg_helpers.h"
namespace Plasma
{
@ -474,14 +475,9 @@ void FrameSvg::getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bot
QRectF FrameSvg::contentsRect() const
{
QSizeF size(frameSize());
if (size.isValid()) {
QRectF rect(QPointF(0, 0), size);
FrameData *frame = d->frames[d->prefix];
return rect.adjusted(frame->leftMargin, frame->topMargin,
-frame->rightMargin, -frame->bottomMargin);
QHash<QString, FrameData *>::const_iterator it = d->frames.constFind(d->prefix);
if (it != d->frames.constEnd()) {
return d->contentGeometry(*it, (*it)->frameSize);
} else {
return QRectF();
}
@ -814,21 +810,21 @@ void FrameSvgPrivate::generateFrameBackground(FrameData *frame)
p.setRenderHint(QPainter::SmoothPixmapTransform);
QRect contentRect = contentGeometry(frame, size);
paintCenter(p, frame, contentRect.size(), size);
paintCenter(p, frame, contentRect, size);
paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::TopBorder, QRect(QPoint(0, 0), QSize(frame->leftWidth, frame->topHeight)));
paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::TopBorder, QRect(QPoint(contentRect.right(), 0), QSize(frame->rightWidth, frame->topHeight)));
paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::BottomBorder, QRect(QPoint(0, contentRect.bottom()), QSize(frame->leftWidth, frame->bottomHeight)));
paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::BottomBorder, QRect(contentRect.bottomRight(), QSize(frame->rightWidth, frame->bottomHeight)));
paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::TopBorder, contentRect);
paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::TopBorder, contentRect);
paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::BottomBorder, contentRect);
paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::BottomBorder, contentRect);
// Sides
const int leftHeight = q->elementSize(prefix % "left").height();
paintBorder(p, frame, FrameSvg::LeftBorder, QSize(frame->leftWidth, leftHeight), QRect(QPoint(0, contentRect.top()), QSize(frame->leftWidth, contentRect.height())));
paintBorder(p, frame, FrameSvg::RightBorder, QSize(frame->rightWidth, leftHeight), QRect(contentRect.topRight(), QSize(frame->rightWidth, contentRect.height())));
paintBorder(p, frame, FrameSvg::LeftBorder, QSize(frame->leftWidth, leftHeight), contentRect);
paintBorder(p, frame, FrameSvg::RightBorder, QSize(frame->rightWidth, leftHeight), contentRect);
const int topWidth = q->elementSize(prefix % "top").width();
paintBorder(p, frame, FrameSvg::TopBorder, QSize(topWidth, frame->topHeight), QRect(QPoint(contentRect.left(), 0), QSize(contentRect.width(), frame->topHeight)));
paintBorder(p, frame, FrameSvg::BottomBorder, QSize(topWidth, frame->bottomHeight), QRect(contentRect.bottomLeft(), QSize(contentRect.width(), frame->bottomHeight)));
paintBorder(p, frame, FrameSvg::TopBorder, QSize(topWidth, frame->topHeight), contentRect);
paintBorder(p, frame, FrameSvg::BottomBorder, QSize(topWidth, frame->bottomHeight), contentRect);
}
QRect FrameSvgPrivate::contentGeometry(FrameData* frame, const QSize& size) const
@ -846,9 +842,9 @@ QRect FrameSvgPrivate::contentGeometry(FrameData* frame, const QSize& size) cons
return contentRect;
}
void FrameSvgPrivate::paintCenter(QPainter& p, FrameData* frame, const QSize& contentSize, const QSize& fullSize)
void FrameSvgPrivate::paintCenter(QPainter& p, FrameData* frame, const QRect& contentRect, const QSize& fullSize)
{
if (!contentSize.isEmpty()) {
if (!contentRect.isEmpty()) {
const QString centerElementId = prefix % "center";
if (frame->tileCenter) {
QSize centerTileSize = q->elementSize(centerElementId);
@ -862,15 +858,14 @@ void FrameSvgPrivate::paintCenter(QPainter& p, FrameData* frame, const QSize& co
if (frame->composeOverBorder) {
p.drawTiledPixmap(QRect(QPoint(0, 0), fullSize), center);
} else {
p.drawTiledPixmap(QRect(QPoint(frame->leftWidth, frame->topHeight), contentSize), center);
p.drawTiledPixmap(FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize), center);
}
} else {
if (frame->composeOverBorder) {
q->paint(&p, QRect(QPoint(0, 0), fullSize),
centerElementId);
} else {
q->paint(&p, QRect(QPoint(frame->leftWidth, frame->topHeight), contentSize),
centerElementId);
q->paint(&p, FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize), centerElementId);
}
}
}
@ -882,12 +877,12 @@ void FrameSvgPrivate::paintCenter(QPainter& p, FrameData* frame, const QSize& co
}
}
void FrameSvgPrivate::paintBorder(QPainter& p, FrameData* frame, const FrameSvg::EnabledBorders borders, const QSize& size, const QRect& output) const
void FrameSvgPrivate::paintBorder(QPainter& p, FrameData* frame, const FrameSvg::EnabledBorders borders, const QSize& size, const QRect& contentRect) const
{
QString side = prefix % borderToElementId(borders);
QString side = prefix % FrameSvgHelpers::borderToElementId(borders);
if (frame->enabledBorders & borders && q->hasElement(side) && !size.isEmpty()) {
if (frame->stretchBorders) {
q->paint(&p, output, side);
q->paint(&p, FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize), side);
} else {
QPixmap px(size);
px.fill(Qt::transparent);
@ -896,46 +891,19 @@ void FrameSvgPrivate::paintBorder(QPainter& p, FrameData* frame, const FrameSvg:
sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
q->paint(&sidePainter, QRect(QPoint(0, 0), size), side);
p.drawTiledPixmap(output, px);
p.drawTiledPixmap(FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize), px);
}
}
}
void FrameSvgPrivate::paintCorner(QPainter& p, FrameData* frame, Plasma::FrameSvg::EnabledBorders border, const QRect& output) const
void FrameSvgPrivate::paintCorner(QPainter& p, FrameData* frame, Plasma::FrameSvg::EnabledBorders border, const QRect& contentRect) const
{
QString corner = prefix % borderToElementId(border);
QString corner = prefix % FrameSvgHelpers::borderToElementId(border);
if (frame->enabledBorders & border && q->hasElement(corner)) {
q->paint(&p, output, corner);
q->paint(&p, FrameSvgHelpers::sectionRect(border, contentRect, frame->frameSize), corner);
}
}
QString FrameSvgPrivate::borderToElementId(FrameSvg::EnabledBorders borders)
{
switch(borders) {
case FrameSvg::NoBorder:
return QString();
case FrameSvg::TopBorder:
return QStringLiteral("top");
case FrameSvg::BottomBorder:
return QStringLiteral("bottom");
case FrameSvg::LeftBorder:
return QStringLiteral("left");
case FrameSvg::RightBorder:
return QStringLiteral("right");
case FrameSvg::TopBorder | FrameSvg::LeftBorder:
return QStringLiteral("topleft");
case FrameSvg::TopBorder | FrameSvg::RightBorder:
return QStringLiteral("topright");
case FrameSvg::BottomBorder | FrameSvg::LeftBorder:
return QStringLiteral("bottomleft");
case FrameSvg::BottomBorder | FrameSvg::RightBorder:
return QStringLiteral("bottomright");
default:
qWarning() << "unrecognized border" << borders;
}
return QString();
}
QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const
{
const QSize size = frameSize(frame).toSize();
@ -1127,6 +1095,11 @@ int FrameData::refcount() const
return references.count();
}
QString FrameSvg::actualPrefix() const
{
return d->prefix;
}
} // Plasma namespace
#include "moc_framesvg.cpp"

View File

@ -287,6 +287,11 @@ public:
*/
Q_INVOKABLE void paintFrame(QPainter *painter, const QPointF &pos = QPointF(0, 0));
/**
* @returns the prefix that is actually used
*/
QString actualPrefix() const;
private:
FrameSvgPrivate *const d;
friend class FrameData;

View File

@ -0,0 +1,100 @@
/*
* Copyright 2014 by Aleix Pol Gonzalez <aleixpol@blue-systems.com>
*
* 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.
*/
#ifndef FRAMESVG_HELPERS_H
#define FRAMESVG_HELPERS_H
#include "framesvg.h"
namespace Plasma
{
namespace FrameSvgHelpers
{
/**
* @returns the element id name for said @p borders
*/
QString borderToElementId(FrameSvg::EnabledBorders borders)
{
switch(borders) {
case FrameSvg::NoBorder:
return QStringLiteral("center");
case FrameSvg::TopBorder:
return QStringLiteral("top");
case FrameSvg::BottomBorder:
return QStringLiteral("bottom");
case FrameSvg::LeftBorder:
return QStringLiteral("left");
case FrameSvg::RightBorder:
return QStringLiteral("right");
case FrameSvg::TopBorder | FrameSvg::LeftBorder:
return QStringLiteral("topleft");
case FrameSvg::TopBorder | FrameSvg::RightBorder:
return QStringLiteral("topright");
case FrameSvg::BottomBorder | FrameSvg::LeftBorder:
return QStringLiteral("bottomleft");
case FrameSvg::BottomBorder | FrameSvg::RightBorder:
return QStringLiteral("bottomright");
default:
qWarning() << "unrecognized border" << borders;
}
return QString();
}
/**
* @returns the suggested geometry for the @p borders given a @p fullSize frame size and a @p contentRect
*/
QRect sectionRect(Plasma::FrameSvg::EnabledBorders borders, const QRect& contentRect, const QSize& fullSize)
{
//don't use QRect corner methods here, they have semantics that might come as unexpected.
//prefer constructing the points explicitly. e.g. from QRect::topRight docs:
//Note that for historical reasons this function returns QPoint(left() + width() -1, top()).
switch(borders) {
case FrameSvg::NoBorder:
return contentRect;
case FrameSvg::TopBorder:
return QRect(QPoint(contentRect.left(), 0), QSize(contentRect.width(), contentRect.top()));
case FrameSvg::BottomBorder:
return QRect(QPoint(contentRect.left(), contentRect.bottom()+1), QSize(contentRect.width(), fullSize.height()-contentRect.bottom()-1));
case FrameSvg::LeftBorder:
return QRect(QPoint(0, contentRect.top()), QSize(contentRect.left(), contentRect.height()));
case FrameSvg::RightBorder:
return QRect(QPoint(contentRect.right()+1, contentRect.top()), QSize(fullSize.width()-contentRect.right()-1, contentRect.height()));
case FrameSvg::TopBorder | FrameSvg::LeftBorder:
return QRect(QPoint(0, 0), QSize(contentRect.left(), contentRect.top()));
case FrameSvg::TopBorder | FrameSvg::RightBorder:
return QRect(QPoint(contentRect.right()+1, 0), QSize(fullSize.width()-contentRect.right()-1, contentRect.top()));
case FrameSvg::BottomBorder | FrameSvg::LeftBorder:
return QRect(QPoint(0, contentRect.bottom()+1), QSize(contentRect.left(), fullSize.height()-contentRect.bottom()-1));
case FrameSvg::BottomBorder | FrameSvg::RightBorder:
return QRect(QPoint(contentRect.right()+1, contentRect.bottom()+1), QSize(fullSize.width()-contentRect.right()-1, fullSize.height()-contentRect.bottom()-1));
default:
qWarning() << "unrecognized border" << borders;
return QRect();
}
return QRect();
}
}
}
#endif

View File

@ -150,8 +150,7 @@ public:
QSizeF frameSize(FrameData *frame) const;
void paintBorder(QPainter& p, FrameData* frame, Plasma::FrameSvg::EnabledBorders border, const QSize& originalSize, const QRect& output) const;
void paintCorner(QPainter& p, FrameData* frame, Plasma::FrameSvg::EnabledBorders border, const QRect& output) const;
void paintCenter(QPainter& p, FrameData* frame, const QSize& contentSize, const QSize& fullSize);
static QString borderToElementId(Plasma::FrameSvg::EnabledBorders borders);
void paintCenter(QPainter& p, FrameData* frame, const QRect& contentRect, const QSize& fullSize);
QRect contentGeometry(FrameData* frame, const QSize& size) const;
Types::Location location;

39
tests/dialog.qml Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright 2014 David Edmundson <davidedmundson@kde.org>
*
* 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,
* 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.
*/
import QtQuick 2.0
import QtQuick.Controls 1.1 as Controls
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
PlasmaCore.Dialog {
visible: true
mainItem: Item {
width: 200
height: 200
MouseArea {
anchors.fill: parent
onClicked: Qt.quit()
}
}
}

106
tests/testborders.qml Normal file
View File

@ -0,0 +1,106 @@
/*
* Copyright (C) 2014 by Aleix Pol Gonzalez <aleixpol@blue-systems.com>
*
* 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.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
Item
{
width: 500
height: 500
PlasmaCore.FrameSvgItem {
id: theItem
imagePath: "widgets/background"
anchors {
fill: parent
margins: 10
}
PlasmaComponents.Button {
text: "left"
checkable: true
checked: true
anchors {
horizontalCenterOffset: -50
centerIn: parent
}
onClicked: {
if (checked)
theItem.enabledBorders |= PlasmaCore.FrameSvg.LeftBorder;
else
theItem.enabledBorders &=~PlasmaCore.FrameSvg.LeftBorder;
}
}
PlasmaComponents.Button {
text: "right"
checkable: true
checked: true
anchors {
horizontalCenterOffset: 50
centerIn: parent
}
onClicked: {
if (checked)
theItem.enabledBorders |= PlasmaCore.FrameSvg.RightBorder;
else
theItem.enabledBorders &=~PlasmaCore.FrameSvg.RightBorder;
}
}
PlasmaComponents.Button {
text: "top"
checkable: true
checked: true
anchors {
verticalCenterOffset: -50
centerIn: parent
}
onClicked: {
if (checked)
theItem.enabledBorders |= PlasmaCore.FrameSvg.TopBorder;
else
theItem.enabledBorders &=~PlasmaCore.FrameSvg.TopBorder;
}
}
PlasmaComponents.Button {
text: "bottom"
checkable: true
checked: true
anchors {
verticalCenterOffset: 50
centerIn: parent
}
onClicked: {
if (checked)
theItem.enabledBorders |= PlasmaCore.FrameSvg.BottomBorder;
else
theItem.enabledBorders &=~PlasmaCore.FrameSvg.BottomBorder;
}
}
}
}