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

KDE's Doxygen guidelines are available online.