@@ -454,6 +454,115 @@ void bindStandardIcon(QAbstractButton* btn, QStyle::StandardPixmap sp)
454454 qApp->installEventFilter (filter);
455455}
456456
457+ QIcon makeThemedIcon (CustomIcon icon, const QColor& color, int size)
458+ {
459+ QPixmap pm (size, size);
460+ pm.fill (Qt::transparent);
461+ QPainter p (&pm);
462+ p.setRenderHint (QPainter::Antialiasing);
463+ p.setPen (Qt::NoPen);
464+ p.setBrush (color);
465+
466+ const qreal m = size * 0.15 ;
467+ const QRectF full (m, m, size - 2 * m, size - 2 * m);
468+
469+ const qreal bar = full.width () * 0.16 ; // thickness of the "end" bar (full is square)
470+ const qreal gap = full.width () * 0.10 ; // space between the bar and the arrow
471+
472+ // Fill a vertical shaft+head arrow (single polygon, no seam) inside the sub-rect.
473+ auto drawVArrow = [&](const QRectF& r, bool up) {
474+ const qreal shaftW = full.width () * 0.38 ;
475+ const qreal shaftOff = (r.width () - shaftW) / 2.0 ;
476+ const qreal headLen = r.height () * 0.55 ;
477+ const qreal cx = r.center ().x ();
478+ QPainterPath path;
479+ if (up) {
480+ const qreal headY = r.top () + headLen;
481+ path.moveTo (cx, r.top ()); // tip
482+ path.lineTo (r.right (), headY); // head right corner
483+ path.lineTo (r.left () + shaftOff + shaftW, headY); // shoulder right
484+ path.lineTo (r.left () + shaftOff + shaftW, r.bottom ()); // shaft bottom right
485+ path.lineTo (r.left () + shaftOff, r.bottom ()); // shaft bottom left
486+ path.lineTo (r.left () + shaftOff, headY); // shoulder left
487+ path.lineTo (r.left (), headY); // head left corner
488+ } else {
489+ const qreal headY = r.bottom () - headLen;
490+ path.moveTo (r.left () + shaftOff, r.top ()); // shaft top left
491+ path.lineTo (r.left () + shaftOff + shaftW, r.top ()); // shaft top right
492+ path.lineTo (r.left () + shaftOff + shaftW, headY); // shoulder right
493+ path.lineTo (r.right (), headY); // head right corner
494+ path.lineTo (cx, r.bottom ()); // tip
495+ path.lineTo (r.left (), headY); // head left corner
496+ path.lineTo (r.left () + shaftOff, headY); // shoulder left
497+ }
498+ path.closeSubpath ();
499+ p.drawPath (path);
500+ };
501+
502+ // Fill a horizontal shaft+head arrow (single polygon, no seam) inside the sub-rect.
503+ auto drawHArrow = [&](const QRectF& r, bool left) {
504+ const qreal shaftW = full.height () * 0.38 ;
505+ const qreal shaftOff = (r.height () - shaftW) / 2.0 ;
506+ const qreal headLen = r.width () * 0.55 ;
507+ const qreal cy = r.center ().y ();
508+ QPainterPath path;
509+ if (left) {
510+ const qreal headX = r.left () + headLen;
511+ path.moveTo (r.left (), cy); // tip
512+ path.lineTo (headX, r.top ()); // head top corner
513+ path.lineTo (headX, r.top () + shaftOff); // shoulder top
514+ path.lineTo (r.right (), r.top () + shaftOff); // shaft right top
515+ path.lineTo (r.right (), r.top () + shaftOff + shaftW); // shaft right bottom
516+ path.lineTo (headX, r.top () + shaftOff + shaftW); // shoulder bottom
517+ path.lineTo (headX, r.bottom ()); // head bottom corner
518+ } else {
519+ const qreal headX = r.right () - headLen;
520+ path.moveTo (r.left (), r.top () + shaftOff); // shaft left top
521+ path.lineTo (headX, r.top () + shaftOff); // shoulder top
522+ path.lineTo (headX, r.top ()); // head top corner
523+ path.lineTo (r.right (), cy); // tip
524+ path.lineTo (headX, r.bottom ()); // head bottom corner
525+ path.lineTo (headX, r.top () + shaftOff + shaftW); // shoulder bottom
526+ path.lineTo (r.left (), r.top () + shaftOff + shaftW); // shaft left bottom
527+ }
528+ path.closeSubpath ();
529+ p.drawPath (path);
530+ };
531+
532+ switch (icon) {
533+ case CustomIcon::MoveToTop:
534+ p.drawRect (QRectF (full.left (), full.top (), full.width (), bar));
535+ drawVArrow (QRectF (full.left (), full.top () + bar + gap, full.width (), full.height () - bar - gap), true );
536+ break ;
537+ case CustomIcon::MoveToBottom:
538+ p.drawRect (QRectF (full.left (), full.bottom () - bar, full.width (), bar));
539+ drawVArrow (QRectF (full.left (), full.top (), full.width (), full.height () - bar - gap), false );
540+ break ;
541+ case CustomIcon::MoveToLeft:
542+ p.drawRect (QRectF (full.left (), full.top (), bar, full.height ()));
543+ drawHArrow (QRectF (full.left () + bar + gap, full.top (), full.width () - bar - gap, full.height ()), true );
544+ break ;
545+ case CustomIcon::MoveToRight:
546+ p.drawRect (QRectF (full.right () - bar, full.top (), bar, full.height ()));
547+ drawHArrow (QRectF (full.left (), full.top (), full.width () - bar - gap, full.height ()), false );
548+ break ;
549+ }
550+
551+ p.end ();
552+ return {pm};
553+ }
554+
555+ void bindCustomIcon (QAbstractButton* btn, CustomIcon icon)
556+ {
557+ auto refresh = [btn, icon]() {
558+ const QColor color = qApp->palette ().color (QPalette::ButtonText);
559+ btn->setIcon (makeThemedIcon (icon, color));
560+ };
561+ refresh ();
562+ auto * filter = new PaletteChangeFilter (btn, refresh);
563+ qApp->installEventFilter (filter);
564+ }
565+
457566void bindThemeIcon (QAction* action, const QString& baseName)
458567{
459568 auto refresh = [action, baseName]() {
0 commit comments