Plasma

framesvg.cpp
1 /*
2  SPDX-FileCopyrightText: 2008-2010 Aaron Seigo <[email protected]>
3  SPDX-FileCopyrightText: 2008-2010 Marco Martin <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "framesvg.h"
9 #include "private/framesvg_p.h"
10 
11 #include <QAtomicInt>
12 #include <QBitmap>
13 #include <QCryptographicHash>
14 #include <QPainter>
15 #include <QRegion>
16 #include <QSize>
17 #include <QStringBuilder>
18 #include <QTimer>
19 
20 #include <QDebug>
21 
22 #include "theme.h"
23 #include "private/svg_p.h"
24 #include "private/framesvg_helpers.h"
25 #include "debug_p.h"
26 
27 namespace Plasma
28 {
29 
30 QHash<ThemePrivate *, QHash<uint, QWeakPointer<FrameData>> > FrameSvgPrivate::s_sharedFrames;
31 
32 // Any attempt to generate a frame whose width or height is larger than this
33 // will be rejected
34 static const int MAX_FRAME_SIZE = 100000;
35 
36 FrameData::~FrameData()
37 {
38  FrameSvgPrivate::s_sharedFrames[theme].remove(cacheId);
39 }
40 
42  : Svg(parent),
43  d(new FrameSvgPrivate(this))
44 {
45  connect(this, &FrameSvg::repaintNeeded, this, std::bind(&FrameSvgPrivate::updateNeeded, d));
46 }
47 
48 FrameSvg::~FrameSvg()
49 {
50  delete d;
51 }
52 
54 {
55  if (path == imagePath()) {
56  return;
57  }
58 
59  clearCache();
60 
62  Svg::d->setImagePath(path);
63  if (!d->repaintBlocked) {
64  d->updateFrameData(Svg::d->lastModified);
65  }
66 }
67 
69 {
70  if (borders == d->enabledBorders) {
71  return;
72  }
73 
74  d->enabledBorders = borders;
75 
76  if (!d->repaintBlocked) {
77  d->updateFrameData(Svg::d->lastModified);
78  }
79 }
80 
82 {
83  return d->enabledBorders;
84 }
85 
87 {
88  switch (location) {
89  case Types::TopEdge:
90  setElementPrefix(QStringLiteral("north"));
91  break;
92  case Types::BottomEdge:
93  setElementPrefix(QStringLiteral("south"));
94  break;
95  case Types::LeftEdge:
96  setElementPrefix(QStringLiteral("west"));
97  break;
98  case Types::RightEdge:
99  setElementPrefix(QStringLiteral("east"));
100  break;
101  default:
103  break;
104  }
105 
106  d->location = location;
107 }
108 
110 {
111  if (prefix.isEmpty() || !hasElement(prefix % QLatin1String("-center"))) {
112  d->prefix.clear();
113  } else {
114  d->prefix = prefix;
115  if (!d->prefix.isEmpty()) {
116  d->prefix += QLatin1Char('-');
117  }
118  }
119  d->requestedPrefix = prefix;
120 
121  d->location = Types::Floating;
122 
123  if (!d->repaintBlocked) {
124  d->updateFrameData(Svg::d->lastModified);
125  }
126 }
127 
129 {
130  //for now it simply checks if a center element exists,
131  //because it could make sense for certain themes to not have all the elements
132  if (prefix.isEmpty()) {
133  return hasElement(QStringLiteral("center"));
134  }
135  if (prefix.endsWith(QLatin1Char('-'))) {
136  return hasElement(prefix % QLatin1String("center"));
137  }
138 
139  return hasElement(prefix % QLatin1String("-center"));
140 }
141 
143 {
144  switch (location) {
145  case Types::TopEdge:
146  return hasElementPrefix(QStringLiteral("north"));
147  case Types::BottomEdge:
148  return hasElementPrefix(QStringLiteral("south"));
149  case Types::LeftEdge:
150  return hasElementPrefix(QStringLiteral("west"));
151  case Types::RightEdge:
152  return hasElementPrefix(QStringLiteral("east"));
153  default:
154  return hasElementPrefix(QString());
155  }
156 }
157 
159 {
160  return d->requestedPrefix;
161 }
162 
164 {
165  if (imagePath().isEmpty()) {
166  return;
167  }
168 
169  if (size.isEmpty()) {
170 #ifndef NDEBUG
171  // qCDebug(LOG_PLASMA) << "Invalid size" << size;
172 #endif
173  return;
174  }
175 
176  if (d->frame && size.toSize() == d->frame->frameSize) {
177  return;
178  }
179  d->pendingFrameSize = size.toSize();
180 
181  if (!d->repaintBlocked) {
182  d->updateFrameData(Svg::d->lastModified, FrameSvgPrivate::UpdateFrame);
183  }
184 }
185 
187 {
188  if (!d->frame) {
189  return QSize(-1, -1);
190  } else {
191  return d->frameSize(d->frame.data());
192  }
193 }
194 
196 {
197  if (!d->frame) {
198  return .0;
199  }
200 
201  if (d->frame->noBorderPadding) {
202  return .0;
203  }
204 
205  switch (edge) {
207  return d->frame->topMargin;
208 
210  return d->frame->leftMargin;
211 
213  return d->frame->rightMargin;
214 
215  //Plasma::BottomMargin
216  default:
217  return d->frame->bottomMargin;
218  }
219 }
220 
222 {
223  if (!d->frame) {
224  return .0;
225  }
226 
227  if (d->frame->noBorderPadding) {
228  return .0;
229  }
230 
231  switch (edge) {
233  return d->frame->insetTopMargin;
234 
236  return d->frame->insetLeftMargin;
237 
239  return d->frame->insetRightMargin;
240 
241  //Plasma::BottomMargin
242  default:
243  return d->frame->insetBottomMargin;
244  }
245 }
246 
248 {
249  if (!d->frame) {
250  return .0;
251  }
252 
253  if (d->frame->noBorderPadding) {
254  return .0;
255  }
256 
257  switch (edge) {
259  return d->frame->fixedTopMargin;
260 
262  return d->frame->fixedLeftMargin;
263 
265  return d->frame->fixedRightMargin;
266 
267  //Plasma::BottomMargin
268  default:
269  return d->frame->fixedBottomMargin;
270  }
271 }
272 
273 void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
274 {
275  if (!d->frame || d->frame->noBorderPadding) {
276  left = top = right = bottom = 0;
277  return;
278  }
279 
280  top = d->frame->topMargin;
281  left = d->frame->leftMargin;
282  right = d->frame->rightMargin;
283  bottom = d->frame->bottomMargin;
284 }
285 
286 void FrameSvg::getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
287 {
288  if (!d->frame || d->frame->noBorderPadding) {
289  left = top = right = bottom = 0;
290  return;
291  }
292 
293  top = d->frame->fixedTopMargin;
294  left = d->frame->fixedLeftMargin;
295  right = d->frame->fixedRightMargin;
296  bottom = d->frame->fixedBottomMargin;
297 }
298 
299 void FrameSvg::getInset(qreal &left, qreal &top, qreal &right, qreal &bottom) const
300 {
301  if (!d->frame || d->frame->noBorderPadding) {
302  left = top = right = bottom = 0;
303  return;
304  }
305 
306  top = d->frame->insetTopMargin;
307  left = d->frame->insetLeftMargin;
308  right = d->frame->insetRightMargin;
309  bottom = d->frame->insetBottomMargin;
310 }
311 
313 {
314  if (d->frame) {
315  QRectF rect(QPoint(0,0), d->frame->frameSize);
316  return rect.adjusted(d->frame->leftMargin, d->frame->topMargin, -d->frame->rightMargin, -d->frame->bottomMargin);
317  } else {
318  return QRectF();
319  }
320 }
321 
323 {
324  //FIXME: the distinction between overlay and
325  return d->alphaMask();
326 }
327 
329 {
330  QRegion result;
331  if (!d->frame) {
332  return result;
333  }
334 
335  uint id = qHash(d->cacheId(d->frame.data(), QString()), SvgRectsCache::s_seed);
336 
337  QRegion* obj = d->frame->cachedMasks.object(id);
338 
339  if (!obj) {
340  obj = new QRegion(QBitmap(d->alphaMask().mask()));
341  result = *obj;
342  d->frame->cachedMasks.insert(id, obj);
343  }
344  else {
345  result = *obj;
346  }
347  return result;
348 }
349 
351 {
352  if (d->cacheAll && !cache) {
353  clearCache();
354  }
355 
356  d->cacheAll = cache;
357 }
358 
360 {
361  return d->cacheAll;
362 }
363 
365 {
366  if (d->frame) {
367  d->frame->cachedBackground = QPixmap();
368  d->frame->cachedMasks.clear();
369  }
370  if (d->maskFrame) {
371  d->maskFrame->cachedBackground = QPixmap();
372  d->maskFrame->cachedMasks.clear();
373  }
374 }
375 
377 {
378  if (d->frame->cachedBackground.isNull()) {
379  d->generateBackground(d->frame);
380  }
381 
382  return d->frame->cachedBackground;
383 }
384 
385 void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source)
386 {
387  if (d->frame->cachedBackground.isNull()) {
388  d->generateBackground(d->frame);
389  if (d->frame->cachedBackground.isNull()) {
390  return;
391  }
392  }
393 
394  painter->drawPixmap(target, d->frame->cachedBackground, source.isValid() ? source : target);
395 }
396 
397 void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos)
398 {
399  if (d->frame->cachedBackground.isNull()) {
400  d->generateBackground(d->frame);
401  if (d->frame->cachedBackground.isNull()) {
402  return;
403  }
404  }
405 
406  painter->drawPixmap(pos, d->frame->cachedBackground);
407 }
408 
409 //#define DEBUG_FRAMESVG_CACHE
410 FrameSvgPrivate::~FrameSvgPrivate() = default;
411 
412 QPixmap FrameSvgPrivate::alphaMask()
413 {
414  QString maskPrefix;
415 
416  if (q->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"))) {
417  maskPrefix = QStringLiteral("mask-");
418  }
419 
420  if (maskPrefix.isNull()) {
421  if (frame->cachedBackground.isNull()) {
422  generateBackground(frame);
423  }
424  return frame->cachedBackground;
425  }
426 
427  // We are setting the prefix only temporary to generate
428  // the needed mask image
429  const QString maskRequestedPrefix = requestedPrefix.isEmpty() ? QStringLiteral("mask") : maskPrefix % requestedPrefix;
430  maskPrefix = maskPrefix % prefix;
431 
432  if (!maskFrame) {
433  maskFrame = lookupOrCreateMaskFrame(frame, maskPrefix, maskRequestedPrefix);
434  if (!maskFrame->cachedBackground.isNull()) {
435  return maskFrame->cachedBackground;
436  }
437  updateSizes(maskFrame);
438  generateBackground(maskFrame);
439  return maskFrame->cachedBackground;
440  }
441 
442  const bool shouldUpdate = maskFrame->enabledBorders != frame->enabledBorders
443  || maskFrame->frameSize != frameSize(frame.data())
444  || maskFrame->imagePath != frame->imagePath;
445  if (shouldUpdate) {
446  maskFrame = lookupOrCreateMaskFrame(frame, maskPrefix, maskRequestedPrefix);
447  if (!maskFrame->cachedBackground.isNull()) {
448  return maskFrame->cachedBackground;
449  }
450  updateSizes(maskFrame);
451  }
452 
453  if (maskFrame->cachedBackground.isNull()) {
454  generateBackground(maskFrame);
455  }
456 
457  return maskFrame->cachedBackground;
458 }
459 
460 QSharedPointer<FrameData> FrameSvgPrivate::lookupOrCreateMaskFrame(const QSharedPointer<FrameData> &frame, const QString &maskPrefix, const QString &maskRequestedPrefix)
461 {
462  const uint key = qHash(cacheId(frame.data(), maskPrefix));
463  QSharedPointer<FrameData> mask = s_sharedFrames[q->theme()->d].value(key);
464 
465  // See if we can find a suitable candidate in the shared frames.
466  // If there is one, use it.
467  if (mask) {
468  return mask;
469  }
470 
471  mask.reset(new FrameData(*frame.data(), q));
472  mask->prefix = maskPrefix;
473  mask->requestedPrefix = maskRequestedPrefix;
474  mask->theme = q->theme()->d;
475  mask->imagePath = frame->imagePath;
476  mask->enabledBorders = frame->enabledBorders;
477  mask->frameSize = frameSize(frame).toSize();
478  mask->cacheId = key;
479  mask->lastModified = frame->lastModified;
480  s_sharedFrames[q->theme()->d].insert(key, mask);
481 
482  return mask;
483 }
484 
485 void FrameSvgPrivate::generateBackground(const QSharedPointer<FrameData> &frame)
486 {
487  if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(frame->prefix)) {
488  return;
489  }
490 
491  const uint id = qHash(cacheId(frame.data(), frame->prefix));
492 
493  bool frameCached = !frame->cachedBackground.isNull();
494  bool overlayCached = false;
495  //TODO KF6: Kill Overlays
496  const bool overlayAvailable = !frame->prefix.startsWith(QLatin1String("mask-")) && q->hasElement(frame->prefix % QLatin1String("overlay"));
497  QPixmap overlay;
498  if (q->isUsingRenderingCache()) {
499  frameCached = q->theme()->findInCache(QString::number(id), frame->cachedBackground, frame->lastModified) && !frame->cachedBackground.isNull();
500 
501  if (overlayAvailable) {
502  const uint overlayId = qHash(cacheId(frame.data(), frame->prefix % QLatin1String("overlay")));
503  overlayCached = q->theme()->findInCache(QString::number(overlayId), overlay, frame->lastModified) && !overlay.isNull();
504  }
505  }
506 
507  if (!frameCached) {
508  generateFrameBackground(frame);
509  }
510 
511  //Overlays
512  QSize overlaySize;
513  QPoint actualOverlayPos = QPoint(0, 0);
514  if (overlayAvailable && !overlayCached) {
515  overlaySize = q->elementSize(frame->prefix % QLatin1String("overlay"));
516 
517  if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-right"))) {
518  actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width());
519  } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-bottom"))) {
520  actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height());
521  //Stretched or Tiled?
522  } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-stretch"))) {
523  overlaySize = frameSize(frame).toSize();
524  } else {
525  if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal"))) {
526  overlaySize.setWidth(frameSize(frame).width());
527  }
528  if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) {
529  overlaySize.setHeight(frameSize(frame).height());
530  }
531  }
532 
533  overlay = alphaMask();
534  QPainter overlayPainter(&overlay);
536  //Tiling?
537  if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal")) ||
538  q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) {
539 
540  QSize s = q->size();
541  q->resize(q->elementSize(frame->prefix % QLatin1String("overlay")));
542 
543  overlayPainter.drawTiledPixmap(QRect(QPoint(0, 0), overlaySize), q->pixmap(frame->prefix % QLatin1String("overlay")));
544  q->resize(s);
545  } else {
546  q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), frame->prefix % QLatin1String("overlay"));
547  }
548 
549  overlayPainter.end();
550  }
551 
552  if (!frameCached) {
553  cacheFrame(frame->prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap());
554  }
555 
556  if (!overlay.isNull()) {
557  QPainter p(&frame->cachedBackground);
559  p.drawPixmap(actualOverlayPos, overlay, QRect(actualOverlayPos, overlaySize));
560  }
561 }
562 
563 void FrameSvgPrivate::generateFrameBackground(const QSharedPointer<FrameData> &frame)
564 {
565  //qCDebug(LOG_PLASMA) << "generating background";
566  const QSize size = frameSize(frame).toSize() * q->devicePixelRatio();
567 
568  if (!size.isValid()) {
569 #ifndef NDEBUG
570  // qCDebug(LOG_PLASMA) << "Invalid frame size" << size;
571 #endif
572  return;
573  }
574  if (size.width() >= MAX_FRAME_SIZE || size.height() >= MAX_FRAME_SIZE) {
575  qCWarning(LOG_PLASMA) << "Not generating frame background for a size whose width or height is more than" << MAX_FRAME_SIZE << size;
576  return;
577  }
578 
579  frame->cachedBackground = QPixmap(size);
580  frame->cachedBackground.fill(Qt::transparent);
581  QPainter p(&frame->cachedBackground);
584 
585  QRect contentRect = contentGeometry(frame, size);
586  paintCenter(p, frame, contentRect, size);
587 
588  paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::TopBorder, contentRect);
589  paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::TopBorder, contentRect);
590  paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::BottomBorder, contentRect);
591  paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::BottomBorder, contentRect);
592 
593  // Sides
594  const int leftHeight = q->elementSize(frame->prefix % QLatin1String("left")).height();
595  paintBorder(p, frame, FrameSvg::LeftBorder, QSize(frame->leftWidth, leftHeight) * q->devicePixelRatio(), contentRect);
596  const int rightHeight = q->elementSize(frame->prefix % QLatin1String("right")).height();
597  paintBorder(p, frame, FrameSvg::RightBorder, QSize(frame->rightWidth, rightHeight) * q->devicePixelRatio(), contentRect);
598 
599  const int topWidth = q->elementSize(frame->prefix % QLatin1String("top")).width();
600  paintBorder(p, frame, FrameSvg::TopBorder, QSize(topWidth, frame->topHeight) * q->devicePixelRatio(), contentRect);
601  const int bottomWidth = q->elementSize(frame->prefix % QLatin1String("bottom")).width();
602  paintBorder(p, frame, FrameSvg::BottomBorder, QSize(bottomWidth, frame->bottomHeight) * q->devicePixelRatio(), contentRect);
603  p.end();
604 
605  frame->cachedBackground.setDevicePixelRatio(q->devicePixelRatio());
606 }
607 
608 QRect FrameSvgPrivate::contentGeometry(const QSharedPointer<FrameData> &frame, const QSize& size) const
609 {
610  const QSize contentSize(size.width() - frame->leftWidth * q->devicePixelRatio() - frame->rightWidth * q->devicePixelRatio(),
611  size.height() - frame->topHeight * q->devicePixelRatio() - frame->bottomHeight * q->devicePixelRatio());
612  QRect contentRect(QPoint(0,0), contentSize);
613  if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(frame->prefix % QLatin1String("left"))) {
614  contentRect.translate(frame->leftWidth * q->devicePixelRatio(), 0);
615  }
616 
617  // Corners
618  if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(frame->prefix % QLatin1String("top"))) {
619  contentRect.translate(0, frame->topHeight * q->devicePixelRatio());
620  }
621  return contentRect;
622 }
623 
624 void FrameSvgPrivate::updateFrameData(uint lastModified, UpdateType updateType)
625 {
626  auto fd = frame;
627  uint newKey = 0;
628 
629  if (fd) {
630  const uint oldKey = fd->cacheId;
631 
632  const QString oldPath = fd->imagePath;
633  const FrameSvg::EnabledBorders oldBorders = fd->enabledBorders;
634  const QSize currentSize = fd->frameSize;
635 
636  fd->enabledBorders = enabledBorders;
637  fd->frameSize = pendingFrameSize;
638  fd->imagePath = q->imagePath();
639 
640  newKey = qHash(cacheId(fd.data(), prefix));
641 
642  //reset frame to old values
643  fd->enabledBorders = oldBorders;
644  fd->frameSize = currentSize;
645  fd->imagePath = oldPath;
646 
647  //FIXME: something more efficient than string comparison?
648  if (oldKey == newKey) {
649  return;
650  }
651 
652  //qCDebug(LOG_PLASMA) << "looking for" << newKey;
653  auto newFd = FrameSvgPrivate::s_sharedFrames[q->theme()->d].value(newKey);
654  if (newFd) {
655  //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount;
656  // we've found a match, use that one
657  Q_ASSERT(newKey == newFd.lock()->cacheId);
658  frame = newFd;
659  return;
660  }
661 
662  fd.reset(new FrameData(*fd, q));
663  } else {
664  fd.reset(new FrameData(q, QString()));
665  }
666 
667  frame = fd;
668  fd->prefix = prefix;
669  fd->requestedPrefix = requestedPrefix;
670  //updateSizes();
671  fd->enabledBorders = enabledBorders;
672  fd->frameSize = pendingFrameSize;
673  fd->imagePath = q->imagePath();
674  fd->lastModified = lastModified;
675  //was fd just created empty now?
676  if (newKey == 0) {
677  newKey = qHash(cacheId(fd.data(), prefix));
678  }
679 
680  // we know it isn't in s_sharedFrames due to the check above, so insert it now
681  FrameSvgPrivate::s_sharedFrames[q->theme()->d].insert(newKey, fd);
682  fd->cacheId = newKey;
683  fd->theme = q->theme()->d;
684  if (updateType == UpdateFrameAndMargins) {
685  updateAndSignalSizes();
686  } else {
687  updateSizes(frame);
688  }
689 }
690 
691 void FrameSvgPrivate::paintCenter(QPainter& p, const QSharedPointer<FrameData> &frame, const QRect& contentRect, const QSize& fullSize)
692 {
693  if (!contentRect.isEmpty()) {
694  const QString centerElementId = frame->prefix % QLatin1String("center");
695  if (frame->tileCenter) {
696  QSize centerTileSize = q->elementSize(centerElementId);
697  QPixmap center(centerTileSize);
698  center.fill(Qt::transparent);
699 
700  QPainter centerPainter(&center);
702  q->paint(&centerPainter, QRect(QPoint(0, 0), centerTileSize),centerElementId);
703 
704  if (frame->composeOverBorder) {
705  p.drawTiledPixmap(QRect(QPoint(0, 0), fullSize), center);
706  } else {
707  p.drawTiledPixmap(FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), center);
708  }
709  } else {
710  if (frame->composeOverBorder) {
711  q->paint(&p, QRect(QPoint(0, 0), fullSize),
712  centerElementId);
713  } else {
714  q->paint(&p, FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), centerElementId);
715  }
716  }
717  }
718 
719  if (frame->composeOverBorder) {
721  p.drawPixmap(QRect(QPoint(0, 0), fullSize), alphaMask());
723  }
724 }
725 
726 void FrameSvgPrivate::paintBorder(QPainter& p, const QSharedPointer<FrameData> &frame, const FrameSvg::EnabledBorders borders, const QSize& size, const QRect& contentRect) const
727 {
728  QString side = frame->prefix % FrameSvgHelpers::borderToElementId(borders);
729  if (frame->enabledBorders & borders && q->hasElement(side) && !size.isEmpty()) {
730  if (frame->stretchBorders) {
731  q->paint(&p, FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), side);
732  } else {
733  QPixmap px(size);
734  px.fill(Qt::transparent);
735 
736  QPainter sidePainter(&px);
738  q->paint(&sidePainter, QRect(QPoint(0, 0), size), side);
739 
740  p.drawTiledPixmap(FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), px);
741  }
742  }
743 }
744 
745 void FrameSvgPrivate::paintCorner(QPainter& p, const QSharedPointer<FrameData> &frame, Plasma::FrameSvg::EnabledBorders border, const QRect& contentRect) const
746 {
747  // Draw the corner only if both borders in both directions are enabled.
748  if ((frame->enabledBorders & border) != border) {
749  return;
750  }
751  const QString corner = frame->prefix % FrameSvgHelpers::borderToElementId(border);
752  if (q->hasElement(corner)) {
753  q->paint(&p, FrameSvgHelpers::sectionRect(border, contentRect, frame->frameSize * q->devicePixelRatio()), corner);
754  }
755 }
756 
757 SvgPrivate::CacheId FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const
758 {
759  const QSize size = frameSize(frame).toSize();
760  return SvgPrivate::CacheId{double(size.width()), double(size.height()), frame->imagePath, prefixToSave, q->status(), q->devicePixelRatio(), q->scaleFactor(), q->colorGroup(), (uint)frame->enabledBorders, q->Svg::d->lastModified};
761 }
762 
763 void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay)
764 {
765  if (!q->isUsingRenderingCache()) {
766  return;
767  }
768 
769  //insert background
770  if (!frame) {
771  return;
772  }
773 
774  const uint id = qHash(cacheId(frame.data(), prefixToSave));
775 
776  //qCDebug(LOG_PLASMA)<<"Saving to cache frame"<<id;
777 
778  q->theme()->insertIntoCache(QString::number(id), background, QString::number((qint64)q, 16) % prefixToSave);
779 
780  if (!overlay.isNull()) {
781  //insert overlay
782  const uint overlayId = qHash(cacheId(frame.data(), frame->prefix % QLatin1String("overlay")));
783  q->theme()->insertIntoCache(QString::number(overlayId), overlay, QString::number((qint64)q, 16) % prefixToSave % QLatin1String("overlay"));
784  }
785 }
786 
787 void FrameSvgPrivate::updateSizes(FrameData *frame) const
788 {
789  //qCDebug(LOG_PLASMA) << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix;
790  Q_ASSERT(frame);
791 
792  QSize s = q->size();
793  q->resize();
794  if (!frame->cachedBackground.isNull()) {
795  frame->cachedBackground = QPixmap();
796  }
797 
798  //This has the same size regardless the border is enabled or not
799  frame->fixedTopHeight = q->elementSize(frame->prefix % QLatin1String("top")).height();
800 
801  if (q->hasElement(frame->prefix % QLatin1String("hint-top-margin"))) {
802  frame->fixedTopMargin = q->elementSize(frame->prefix % QLatin1String("hint-top-margin")).height();
803  } else {
804  frame->fixedTopMargin = frame->fixedTopHeight;
805  }
806 
807  //The same, but its size depends from the margin being enabled
808  if (frame->enabledBorders & FrameSvg::TopBorder) {
809  frame->topMargin = frame->fixedTopMargin;
810  frame->topHeight = frame->fixedTopHeight;
811  } else {
812  frame->topMargin = frame->topHeight = 0;
813  }
814 
815  if (q->hasElement(frame->prefix % QLatin1String("hint-top-inset"))) {
816  frame->insetTopMargin = q->elementSize(frame->prefix % QLatin1String("hint-top-inset")).height();
817  } else {
818  frame->insetTopMargin = -1;
819  }
820 
821  frame->fixedLeftWidth = q->elementSize(frame->prefix % QLatin1String("left")).width();
822 
823  if (q->hasElement(frame->prefix % QLatin1String("hint-left-margin"))) {
824  frame->fixedLeftMargin = q->elementSize(frame->prefix % QLatin1String("hint-left-margin")).width();
825  } else {
826  frame->fixedLeftMargin = frame->fixedLeftWidth;
827  }
828 
829  if (frame->enabledBorders & FrameSvg::LeftBorder) {
830  frame->leftMargin = frame->fixedLeftMargin;
831  frame->leftWidth = frame->fixedLeftWidth;
832  } else {
833  frame->leftMargin = frame->leftWidth = 0;
834  }
835 
836  if (q->hasElement(frame->prefix % QLatin1String("hint-left-inset"))) {
837  frame->insetLeftMargin = q->elementSize(frame->prefix % QLatin1String("hint-left-inset")).width();
838  } else {
839  frame->insetLeftMargin = -1;
840  }
841 
842  frame->fixedRightWidth = q->elementSize(frame->prefix % QLatin1String("right")).width();
843 
844  if (q->hasElement(frame->prefix % QLatin1String("hint-right-margin"))) {
845  frame->fixedRightMargin = q->elementSize(frame->prefix % QLatin1String("hint-right-margin")).width();
846  } else {
847  frame->fixedRightMargin = frame->fixedRightWidth;
848  }
849 
850  if (frame->enabledBorders & FrameSvg::RightBorder) {
851  frame->rightMargin = frame->fixedRightMargin;
852  frame->rightWidth = frame->fixedRightWidth;
853  } else {
854  frame->rightMargin = frame->rightWidth = 0;
855  }
856 
857  if (q->hasElement(frame->prefix % QLatin1String("hint-right-inset"))) {
858  frame->insetRightMargin = q->elementSize(frame->prefix % QLatin1String("hint-right-inset")).width();
859  } else {
860  frame->insetRightMargin = -1;
861  }
862 
863  frame->fixedBottomHeight = q->elementSize(frame->prefix % QLatin1String("bottom")).height();
864 
865  if (q->hasElement(frame->prefix % QLatin1String("hint-bottom-margin"))) {
866  frame->fixedBottomMargin = q->elementSize(frame->prefix % QLatin1String("hint-bottom-margin")).height();
867  } else {
868  frame->fixedBottomMargin = frame->fixedBottomHeight;
869  }
870 
871  if (frame->enabledBorders & FrameSvg::BottomBorder) {
872  frame->bottomMargin = frame->fixedBottomMargin;
873  frame->bottomHeight = frame->fixedBottomHeight;
874  } else {
875  frame->bottomMargin = frame->bottomHeight = 0;
876  }
877 
878  if (q->hasElement(frame->prefix % QLatin1String("hint-bottom-inset"))) {
879  frame->insetBottomMargin = q->elementSize(frame->prefix % QLatin1String("hint-bottom-inset")).height();
880  } else {
881  frame->insetBottomMargin = -1;
882  }
883 
884  frame->composeOverBorder = (q->hasElement(frame->prefix % QLatin1String("hint-compose-over-border")) &&
885  q->hasElement(QLatin1String("mask-") % frame->prefix % QLatin1String("center")));
886 
887  //since it's rectangular, topWidth and bottomWidth must be the same
888  //the ones that don't have a frame->prefix is for retrocompatibility
889  frame->tileCenter = (q->hasElement(QStringLiteral("hint-tile-center")) || q->hasElement(frame->prefix % QLatin1String("hint-tile-center")));
890  frame->noBorderPadding = (q->hasElement(QStringLiteral("hint-no-border-padding")) || q->hasElement(frame->prefix % QLatin1String("hint-no-border-padding")));
891  frame->stretchBorders = (q->hasElement(QStringLiteral("hint-stretch-borders")) || q->hasElement(frame->prefix % QLatin1String("hint-stretch-borders")));
892  q->resize(s);
893 }
894 
895 void FrameSvgPrivate::updateNeeded()
896 {
897  q->setElementPrefix(requestedPrefix);
898  //frame not created yet?
899  if (!frame) {
900  return;
901  }
902  q->clearCache();
903  updateSizes(frame);
904 }
905 
906 void FrameSvgPrivate::updateAndSignalSizes()
907 {
908  //frame not created yet?
909  if (!frame) {
910  return;
911  }
912  updateSizes(frame);
913  Q_EMIT q->repaintNeeded();
914 }
915 
916 QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const
917 {
918  if (!frame) {
919  return QSizeF();
920  }
921 
922  if (!frame->frameSize.isValid()) {
923  updateSizes(frame);
924  frame->frameSize = q->size();
925  }
926 
927  return frame->frameSize;
928 }
929 
931 {
932  return d->prefix;
933 }
934 
936 {
937  return d->repaintBlocked;
938 }
939 
940 void FrameSvg::setRepaintBlocked(bool blocked)
941 {
942  d->repaintBlocked = blocked;
943 
944  if (!blocked) {
945  d->updateFrameData(Svg::d->lastModified);
946  }
947 }
948 
949 } // Plasma namespace
Along the top of the screen.
Definition: plasma.h:159
Q_INVOKABLE void paintFrame(QPainter *painter, const QRectF &target, const QRectF &source=QRectF())
Paints the loaded SVG with the elements that represents the border.
Definition: framesvg.cpp:385
Q_INVOKABLE QRegion mask() const
Returns a mask that tightly contains the fully opaque areas of the svg.
Definition: framesvg.cpp:328
FrameSvg(QObject *parent=nullptr)
Constructs a new FrameSvg that paints the proper named subelements as borders.
Definition: framesvg.cpp:41
bool isValid() const const
void setHeight(int height)
Q_INVOKABLE void setCacheAllRenderedFrames(bool cache)
Sets whether saving all the rendered prefixes in a cache or not.
Definition: framesvg.cpp:350
QHash::iterator insert(const Key &key, const T &value)
int width() const const
bool end()
void setCompositionMode(QPainter::CompositionMode mode)
void repaintNeeded()
Emitted whenever the SVG data has changed in such a way that a repaint is required.
void setRenderHint(QPainter::RenderHint hint, bool on)
Q_INVOKABLE qreal fixedMarginSize(const Plasma::Types::MarginEdge edge) const
Returns the margin size given the margin edge we want.
Definition: framesvg.cpp:247
void fill(const QColor &color)
void setRepaintBlocked(bool blocked)
If we will do several changes at once in the frame properties, such as prefix, enabled borders and si...
Definition: framesvg.cpp:940
bool isEmpty() const const
Q_INVOKABLE void getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
Convenience method that extracts the size of the four margins in the four output parameters Compared ...
Definition: framesvg.cpp:286
Along the right side of the screen.
Definition: plasma.h:162
Q_INVOKABLE QRectF contentsRect() const
Definition: framesvg.cpp:312
QSize size() const
The size of the SVG.
void drawTiledPixmap(const QRectF &rectangle, const QPixmap &pixmap, const QPointF &position)
QString imagePath() const
The SVG file to render.
T * data() const const
Namespace for everything in libplasma.
Definition: datamodel.cpp:14
bool isNull() const const
Q_INVOKABLE qreal marginSize(const Plasma::Types::MarginEdge edge) const
Returns the margin size given the margin edge we want If the given margin is disabled, it will return 0.
Definition: framesvg.cpp:195
Q_INVOKABLE bool hasElement(const QString &elementId) const
Check whether an element exists in the loaded SVG.
Definition: svg.cpp:1044
The right margin.
Definition: plasma.h:257
QString number(int n, int base)
bool isEmpty() const const
Q_INVOKABLE void resizeFrame(const QSizeF &size)
Resize the frame maintaining the same border size.
Definition: framesvg.cpp:163
QPixmap alphaMask() const
Definition: framesvg.cpp:322
Along the bottom of the screen.
Definition: plasma.h:160
Along the left side of the screen.
Definition: plasma.h:161
QSize toSize() const const
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
void setWidth(int width)
Q_INVOKABLE bool hasElementPrefix(const QString &prefix) const
Definition: framesvg.cpp:128
bool isEmpty() const const
Q_INVOKABLE QString prefix()
Returns the prefix for SVG elements of the FrameSvg (including a &#39;-&#39; at the end if not empty) ...
Definition: framesvg.cpp:158
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
Q_INVOKABLE void setElementPrefix(Plasma::Types::Location location)
Sets the prefix (.
Definition: framesvg.cpp:86
int remove(const Key &key)
const T value(const Key &key) const const
CompositionMode_SourceIn
bool isNull() const const
KCALENDARCORE_EXPORT uint qHash(const KCalendarCore::Period &key)
bool isEmpty() const const
void setContainsMultipleImages(bool multiple)
Set whether the SVG contains a single image or multiple ones.
Definition: svg.cpp:1073
The top margin.
Definition: plasma.h:254
Q_INVOKABLE void clearCache()
Deletes the internal cache freeing memory: use this if you want to switch the rendered element and yo...
Definition: framesvg.cpp:364
bool isValid() const const
if(recurs()&&!first)
bool isRepaintBlocked() const
Definition: framesvg.cpp:935
Q_INVOKABLE bool cacheAllRenderedFrames() const
Definition: framesvg.cpp:359
Free floating.
Definition: plasma.h:154
Q_INVOKABLE QPixmap framePixmap()
Returns a pixmap of the SVG represented by this object.
Definition: framesvg.cpp:376
EnabledBorders enabledBorders() const
Convenience method to get the enabled borders.
Q_INVOKABLE void setImagePath(const QString &path) override
Loads a new Svg.
Definition: framesvg.cpp:53
int height() const const
Q_INVOKABLE qreal insetSize(const Plasma::Types::MarginEdge edge) const
Returns the insets margin size given the margin edge we want.
Definition: framesvg.cpp:221
Q_INVOKABLE void getInset(qreal &left, qreal &top, qreal &right, qreal &bottom) const
Convenience method that extracts the size of the four inset margins in the four output parameters...
Definition: framesvg.cpp:299
void setX(int x)
void setY(int y)
QRectF adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2) const const
The left margin.
Definition: plasma.h:256
Q_INVOKABLE QSizeF frameSize() const
Definition: framesvg.cpp:186
Q_INVOKABLE void getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
Convenience method that extracts the size of the four margins in the four output parameters The disab...
Definition: framesvg.cpp:273
Location
The Location enumeration describes where on screen an element, such as an Applet or its managing cont...
Definition: plasma.h:153
transparent
bool isNull() const const
SmoothPixmapTransform
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
A theme aware image-centric SVG class.
Definition: svg.h:43
QString actualPrefix() const
Definition: framesvg.cpp:930
Q_EMITQ_EMIT
void setEnabledBorders(const EnabledBorders borders)
Sets what borders should be painted.
Definition: framesvg.cpp:68
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Wed Jan 27 2021 22:40:50 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.