Changeset View
Standalone View
src/desktopicon.cpp
Show First 20 Lines • Show All 154 Lines • ▼ Show 20 Line(s) | |||||
155 | 155 | | |||
156 | void DesktopIcon::setSource(const QVariant &icon) | 156 | void DesktopIcon::setSource(const QVariant &icon) | ||
157 | { | 157 | { | ||
158 | if (m_source == icon) { | 158 | if (m_source == icon) { | ||
159 | return; | 159 | return; | ||
160 | } | 160 | } | ||
161 | m_source = icon; | 161 | m_source = icon; | ||
162 | m_changed = true; | 162 | m_changed = true; | ||
163 | m_monochromeHeuristics.clear(); | ||||
163 | 164 | | |||
164 | if (!m_theme) { | 165 | if (!m_theme) { | ||
165 | m_theme = static_cast<Kirigami::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::PlatformTheme>(this, true)); | 166 | m_theme = static_cast<Kirigami::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::PlatformTheme>(this, true)); | ||
166 | Q_ASSERT(m_theme); | 167 | Q_ASSERT(m_theme); | ||
167 | 168 | | |||
168 | connect(m_theme, &Kirigami::PlatformTheme::colorsChanged, this, [this]() { | 169 | connect(m_theme, &Kirigami::PlatformTheme::colorsChanged, this, [this]() { | ||
169 | m_changed = true; | 170 | m_changed = true; | ||
170 | update(); | 171 | update(); | ||
171 | }); | 172 | }); | ||
172 | } | 173 | } | ||
173 | 174 | | |||
175 | if (icon.type() == QVariant::String) { | ||||
176 | const QString iconSource = icon.toString(); | ||||
177 | m_isMaskHeuristic = (iconSource.endsWith(QStringLiteral("-symbolic")) | ||||
178 | || iconSource.endsWith(QStringLiteral("-symbolic-rtl")) | ||||
179 | || iconSource.endsWith(QStringLiteral("-symbolic-ltr"))); | ||||
180 | emit isMaskChanged(); | ||||
181 | } | ||||
182 | | ||||
174 | if (m_networkReply) { | 183 | if (m_networkReply) { | ||
175 | //if there was a network query going on, interrupt it | 184 | //if there was a network query going on, interrupt it | ||
176 | m_networkReply->close(); | 185 | m_networkReply->close(); | ||
177 | } | 186 | } | ||
178 | m_loadedImage = QImage(); | 187 | m_loadedImage = QImage(); | ||
179 | update(); | 188 | update(); | ||
180 | emit sourceChanged(); | 189 | emit sourceChanged(); | ||
181 | } | 190 | } | ||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Line(s) | |||||
224 | 233 | | |||
225 | void DesktopIcon::setIsMask(bool mask) | 234 | void DesktopIcon::setIsMask(bool mask) | ||
226 | { | 235 | { | ||
227 | if (m_isMask == mask) { | 236 | if (m_isMask == mask) { | ||
228 | return; | 237 | return; | ||
229 | } | 238 | } | ||
230 | 239 | | |||
231 | m_isMask = mask; | 240 | m_isMask = mask; | ||
241 | m_isMaskHeuristic = mask; | ||||
232 | m_changed = true; | 242 | m_changed = true; | ||
233 | update(); | 243 | update(); | ||
234 | emit isMaskChanged(); | 244 | emit isMaskChanged(); | ||
235 | } | 245 | } | ||
236 | 246 | | |||
237 | bool DesktopIcon::isMask() const | 247 | bool DesktopIcon::isMask() const | ||
238 | { | 248 | { | ||
239 | return m_isMask; | 249 | return m_isMask || m_isMaskHeuristic; | ||
240 | } | 250 | } | ||
241 | 251 | | |||
242 | void DesktopIcon::setColor(const QColor &color) | 252 | void DesktopIcon::setColor(const QColor &color) | ||
243 | { | 253 | { | ||
244 | if (m_color == color) { | 254 | if (m_color == color) { | ||
245 | return; | 255 | return; | ||
246 | } | 256 | } | ||
247 | 257 | | |||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Line(s) | 313 | case QVariant::Pixmap: | |||
305 | break; | 315 | break; | ||
306 | case QVariant::Image: | 316 | case QVariant::Image: | ||
307 | img = m_source.value<QImage>(); | 317 | img = m_source.value<QImage>(); | ||
308 | break; | 318 | break; | ||
309 | case QVariant::Bitmap: | 319 | case QVariant::Bitmap: | ||
310 | img = m_source.value<QBitmap>().toImage(); | 320 | img = m_source.value<QBitmap>().toImage(); | ||
311 | break; | 321 | break; | ||
312 | case QVariant::Icon: | 322 | case QVariant::Icon: | ||
313 | img = m_source.value<QIcon>().pixmap(size, iconMode(), QIcon::On).toImage(); | 323 | img = m_source.value<QIcon>().pixmap(size, iconMode(), QIcon::On).toImage(); | ||
davidedmundson: DesktopIcon::setSource takes a QVariant. The path with the colouring is only executed if this… | |||||
314 | break; | 324 | break; | ||
315 | case QVariant::Url: | 325 | case QVariant::Url: | ||
316 | case QVariant::String: | 326 | case QVariant::String: | ||
317 | img = findIcon(size); | 327 | img = findIcon(size); | ||
318 | break; | 328 | break; | ||
319 | case QVariant::Brush: | 329 | case QVariant::Brush: | ||
320 | //todo: fill here too? | 330 | //todo: fill here too? | ||
321 | case QVariant::Color: | 331 | case QVariant::Color: | ||
322 | img = QImage(size, QImage::Format_Alpha8); | 332 | img = QImage(size, QImage::Format_Alpha8); | ||
323 | img.fill(m_source.value<QColor>()); | 333 | img.fill(m_source.value<QColor>()); | ||
324 | break; | 334 | break; | ||
325 | default: | 335 | default: | ||
326 | break; | 336 | break; | ||
327 | } | 337 | } | ||
328 | 338 | | |||
329 | if (img.isNull()){ | 339 | if (img.isNull()){ | ||
330 | img = QImage(size, QImage::Format_Alpha8); | 340 | img = QImage(size, QImage::Format_Alpha8); | ||
331 | img.fill(Qt::transparent); | 341 | img.fill(Qt::transparent); | ||
332 | } | 342 | } | ||
333 | if (img.size() != size){ | 343 | if (img.size() != size) { | ||
334 | // At this point, the image will already be scaled, but we need to output it in | 344 | // At this point, the image will already be scaled, but we need to output it in | ||
335 | // the correct aspect ratio, painted centered in the viewport. So: | 345 | // the correct aspect ratio, painted centered in the viewport. So: | ||
336 | QRect destination(QPoint(0, 0), img.size().scaled(itemSize, Qt::KeepAspectRatio)); | 346 | QRect destination(QPoint(0, 0), img.size().scaled(itemSize, Qt::KeepAspectRatio)); | ||
337 | destination.moveCenter(nodeRect.center()); | 347 | destination.moveCenter(nodeRect.center()); | ||
338 | nodeRect = destination; | 348 | nodeRect = destination; | ||
339 | } | 349 | } | ||
350 | | ||||
351 | const QColor tintColor = !m_color.isValid() || m_color == Qt::transparent ? (m_selected ? m_theme->highlightedTextColor() : m_theme->textColor()) : m_color; | ||||
352 | | ||||
353 | //TODO: initialize m_isMask with icon.isMask() | ||||
354 | if (tintColor.alpha() > 0 && (isMask() || guessMonochrome(img))) { | ||||
355 | QPainter p(&img); | ||||
356 | p.setCompositionMode(QPainter::CompositionMode_SourceIn); | ||||
357 | p.fillRect(img.rect(), tintColor); | ||||
358 | p.end(); | ||||
359 | } | ||||
340 | } | 360 | } | ||
341 | m_changed = false; | 361 | m_changed = false; | ||
342 | 362 | | |||
343 | ManagedTextureNode* mNode = dynamic_cast<ManagedTextureNode*>(node); | 363 | ManagedTextureNode* mNode = dynamic_cast<ManagedTextureNode*>(node); | ||
344 | if (!mNode) { | 364 | if (!mNode) { | ||
345 | delete node; | 365 | delete node; | ||
346 | mNode = new ManagedTextureNode; | 366 | mNode = new ManagedTextureNode; | ||
347 | } | 367 | } | ||
▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Line(s) | 495 | if (isPath) { | |||
476 | icon = QIcon(iconSource); | 496 | icon = QIcon(iconSource); | ||
477 | } else { | 497 | } else { | ||
478 | if (icon.isNull()) { | 498 | if (icon.isNull()) { | ||
479 | icon = m_theme->iconFromTheme(iconSource, m_color); | 499 | icon = m_theme->iconFromTheme(iconSource, m_color); | ||
480 | } | 500 | } | ||
481 | } | 501 | } | ||
482 | if (!icon.isNull()) { | 502 | if (!icon.isNull()) { | ||
483 | img = icon.pixmap(size, iconMode(), QIcon::On).toImage(); | 503 | img = icon.pixmap(size, iconMode(), QIcon::On).toImage(); | ||
484 | qreal ratio = 1; | | |||
485 | if (window() && window()->screen()) { | | |||
486 | ratio = window()->screen()->devicePixelRatio(); | | |||
487 | } | | |||
488 | 504 | | |||
489 | const QColor tintColor = !m_color.isValid() || m_color == Qt::transparent ? (m_selected ? m_theme->highlightedTextColor() : m_theme->textColor()) : m_color; | 505 | /*const QColor tintColor = !m_color.isValid() || m_color == Qt::transparent ? (m_selected ? m_theme->highlightedTextColor() : m_theme->textColor()) : m_color; | ||
Will this code ever get called if we are using the KIconLoader? We want to avoid that. (Even if it's just looking at QIcon.engine()->metaObject) davidedmundson: Will this code ever get called if we are using the KIconLoader? We want to avoid that. (Even… | |||||
490 | 506 | | |||
491 | if (m_isMask || | 507 | if (m_isMask || icon.isMask() || iconSource.endsWith(QStringLiteral("-symbolic")) || iconSource.endsWith(QStringLiteral("-symbolic-rtl")) || iconSource.endsWith(QStringLiteral("-symbolic-ltr")) || guessMonochrome(img)) { | ||
Do we need to test: -symbolic (I saw this in some gnome code) davidedmundson: Do we need to test:
-symbolic
-symbolic-ltr
-symbolic-rtl
(I saw this in some gnome code) | |||||
492 | //this is an heuristic to decide when to tint and when to just draw | | |||
493 | //(fullcolor icons) in reality on basic styles the only colored icons should be -symbolic, this heuristic is the most compatible middle ground | | |||
494 | icon.isMask() || | | |||
495 | //if symbolic color based on tintColor | | |||
496 | (iconSource.endsWith(QLatin1String("-symbolic")) && tintColor.isValid() && tintColor != Qt::transparent) || | | |||
497 | //if path color based on m_color | | |||
498 | (isPath && m_color.isValid() && m_color != Qt::transparent)) { | | |||
499 | QPainter p(&img); | 508 | QPainter p(&img); | ||
500 | p.setCompositionMode(QPainter::CompositionMode_SourceIn); | 509 | p.setCompositionMode(QPainter::CompositionMode_SourceIn); | ||
501 | p.fillRect(img.rect(), tintColor); | 510 | p.fillRect(img.rect(), tintColor); | ||
502 | p.end(); | 511 | p.end(); | ||
503 | } | 512 | }*/ | ||
504 | } | 513 | } | ||
505 | } | 514 | } | ||
506 | return img; | 515 | return img; | ||
507 | } | 516 | } | ||
508 | 517 | | |||
509 | QIcon::Mode DesktopIcon::iconMode() const | 518 | QIcon::Mode DesktopIcon::iconMode() const | ||
510 | { | 519 | { | ||
511 | if (!isEnabled()) { | 520 | if (!isEnabled()) { | ||
512 | return QIcon::Disabled; | 521 | return QIcon::Disabled; | ||
513 | } else if (m_selected) { | 522 | } else if (m_selected) { | ||
514 | return QIcon::Selected; | 523 | return QIcon::Selected; | ||
515 | } else if (m_active) { | 524 | } else if (m_active) { | ||
516 | return QIcon::Active; | 525 | return QIcon::Active; | ||
517 | } | 526 | } | ||
518 | return QIcon::Normal; | 527 | return QIcon::Normal; | ||
519 | } | 528 | } | ||
520 | 529 | | |||
530 | bool DesktopIcon::guessMonochrome(const QImage &img) | ||||
531 | { | ||||
532 | //don't try for too big images | ||||
533 | if (img.width() >= 256 || m_theme->supportsIconColoring()) { | ||||
cfeck: Did you mean `>= 256`? | |||||
534 | return false; | ||||
535 | } | ||||
536 | // round size to a standard size. hardcode as we can't use KIconLoader | ||||
537 | int stdSize; | ||||
538 | if (img.width() <= 16) { | ||||
539 | stdSize = 16; | ||||
540 | } else if (img.width() <= 22) { | ||||
541 | stdSize = 22; | ||||
542 | } else if (img.width() <= 24) { | ||||
543 | stdSize = 24; | ||||
544 | } else if (img.width() <= 32) { | ||||
545 | stdSize = 32; | ||||
546 | } else if (img.width() <= 48) { | ||||
547 | stdSize = 48; | ||||
548 | } else if (img.width() <= 64) { | ||||
549 | stdSize = 64; | ||||
550 | } else { | ||||
551 | stdSize = 128; | ||||
552 | } | ||||
553 | | ||||
554 | auto findIt = m_monochromeHeuristics.constFind(stdSize); | ||||
555 | if (findIt != m_monochromeHeuristics.constEnd()) { | ||||
556 | return findIt.value(); | ||||
You are caching the result per size, but the initial decision depends on the actual icon image, right? Is it possible that the first icon examined is colorful, but the rest is not, or vice versa? If yes, would it make sense to examine a few icons (maybe three) before a decision is made? cfeck: You are caching the result per size, but the initial decision depends on the actual icon image… | |||||
the actual icon image can vary depending on size, usually themes have multiple images per icon separed by those "standard sizes" i'm checking against, so it's possible that like the 16 trough 24 sizes are monochrome and beyond are colored for instance (is often the case in breeze icons) mart: the actual icon image can vary depending on size, usually themes have multiple images per icon… | |||||
557 | } | ||||
558 | | ||||
559 | QHash<int, int> dist; | ||||
560 | int transparentPixels = 0; | ||||
561 | int saturatedPixels = 0; | ||||
562 | for(int x=0; x < img.width(); x++) { | ||||
cfeck: Spaces | |||||
563 | for(int y=0; y < img.height(); y++) { | ||||
564 | QColor color = QColor::fromRgba(qUnpremultiply(img.pixel(x, y))); | ||||
565 | if (color.alpha() < 100) { | ||||
566 | ++transparentPixels; | ||||
567 | continue; | ||||
568 | } else if (color.saturation() > 84) { | ||||
569 | ++saturatedPixels; | ||||
570 | } | ||||
571 | dist[qGray(color.rgb())]++; | ||||
572 | } | ||||
573 | } | ||||
574 | | ||||
575 | QMultiMap<int, int> reverseDist; | ||||
576 | auto it = dist.constBegin(); | ||||
577 | std::vector<qreal> probabilities(dist.size()); | ||||
578 | qreal entropy = 0; | ||||
579 | while (it != dist.constEnd()) { | ||||
580 | reverseDist.insertMulti(it.value(), it.key()); | ||||
581 | qreal probability = qreal(it.value()) / qreal(img.size().width() * img.size().height() - transparentPixels); | ||||
Please add same spaces between binary operators. Also C++ casts are 'type(val)' instead of '(type)val'. cfeck: Please add same spaces between binary operators. Also C++ casts are 'type(val)' instead of '… | |||||
582 | entropy -= probability * log(probability) / log(255); | ||||
583 | ++it; | ||||
584 | } | ||||
585 | | ||||
586 | // Arbitrarly low values of entropy and colored pixels | ||||
587 | m_monochromeHeuristics[stdSize] = saturatedPixels <= (img.size().width()*img.size().height() - transparentPixels) * 0.3 && entropy <= 0.3; | ||||
588 | return m_monochromeHeuristics[stdSize]; | ||||
589 | } | ||||
590 | | ||||
521 | QString DesktopIcon::fallback() const | 591 | QString DesktopIcon::fallback() const | ||
522 | { | 592 | { | ||
523 | return m_fallback; | 593 | return m_fallback; | ||
524 | } | 594 | } | ||
525 | 595 | | |||
526 | void DesktopIcon::setFallback(const QString& fallback) | 596 | void DesktopIcon::setFallback(const QString& fallback) | ||
527 | { | 597 | { | ||
528 | if (m_fallback != fallback) { | 598 | if (m_fallback != fallback) { | ||
529 | m_fallback = fallback; | 599 | m_fallback = fallback; | ||
530 | Q_EMIT fallbackChanged(fallback); | 600 | Q_EMIT fallbackChanged(fallback); | ||
531 | } | 601 | } | ||
532 | } | 602 | } |
DesktopIcon::setSource takes a QVariant. The path with the colouring is only executed if this variant is a string.
If it's already an icon we go to here directly.