Coloring slider handle depending on state
-
I have a custom slider widget class that inherits from QSlider. The slider has an internal state that is set by the application. I want to color the sliders handle depending on that state. I also created a QProxyStyle which this widget uses that modifies the handles rect. The base style is fusion. I tried to copy paste the drawing of the handle from the fusion style into the proxy but I dont understand how to include the private qt classes this style is using. Probably its not required to do this at all. How can I change the handles color without affecting other components of the slider?
-
I have a custom slider widget class that inherits from QSlider. The slider has an internal state that is set by the application. I want to color the sliders handle depending on that state. I also created a QProxyStyle which this widget uses that modifies the handles rect. The base style is fusion. I tried to copy paste the drawing of the handle from the fusion style into the proxy but I dont understand how to include the private qt classes this style is using. Probably its not required to do this at all. How can I change the handles color without affecting other components of the slider?
-
@ian28 I guess you can use style sheet to do it.
https://doc.qt.io/qt-6/stylesheet-examples.html -
This topic describes the same problem link. The marked answer suggests to either derive from QStyleOption or directly query the widget. This is what i tried by copy pasting the drawing of the slider of the fusion style and only modify the background by either querying the widget or using a subclass of the StyleOptionslider. However the fusion style uses private qt classes, how can I include them?
#if QT_CONFIG(slider) case CC_Slider: if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>( option)) { QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); bool horizontal = slider->orientation == Qt::Horizontal; bool ticksAbove = slider->tickPosition & QSlider::TicksAbove; bool ticksBelow = slider->tickPosition & QSlider::TicksBelow; QColor activeHighlight = d->highlight( option->palette); QPixmap cache; QBrush oldBrush = painter->brush(); QPen oldPen = painter->pen(); QColor shadowAlpha(Qt::black); shadowAlpha.setAlpha(10); if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) outline = d->highlightedOutline( option->palette); if ((option->subControls & SC_SliderGroove) && groove.isValid()) { QColor grooveColor; grooveColor.setHsv( buttonColor.hue(), qMin(255, (int)(buttonColor.saturation())), qMin(255, (int)(buttonColor.value()*0.9))); QString groovePixmapName = QStyleHelper::uniqueName("slider_groove"_L1, option, groove.size()); QRect pixmapRect(0, 0, groove.width(), groove.height()); // draw background groove if (!QPixmapCache::find(groovePixmapName, &cache)) { cache = styleCachePixmap( pixmapRect.size()); cache.fill( Qt::transparent); QPainter groovePainter(&cache); groovePainter.setRenderHint(QPainter::Antialiasing, true); groovePainter.translate(0.5, 0.5); QLinearGradient gradient; if (horizontal) { gradient.setStart(pixmapRect.center().x(), pixmapRect.top()); gradient.setFinalStop(pixmapRect.center().x(), pixmapRect.bottom()); } else { gradient.setStart(pixmapRect.left(), pixmapRect.center().y()); gradient.setFinalStop(pixmapRect.right(), pixmapRect.center().y()); } groovePainter.setPen(QPen(outline)); gradient.setColorAt(0, grooveColor.darker( 110)); gradient.setColorAt(1, grooveColor.lighter( 110));//palette.button().color().darker(115)); groovePainter.setBrush(gradient); groovePainter.drawRoundedRect(pixmapRect.adjusted(1, 1, -2, -2), 1, 1); groovePainter.end(); QPixmapCache::insert(groovePixmapName, cache); } painter->drawPixmap(groove.topLeft(), cache); // draw blue groove highlight QRect clipRect; if (!groovePixmapName.isEmpty()) groovePixmapName += "_blue"_L1; if (!QPixmapCache::find(groovePixmapName, &cache)) { cache = styleCachePixmap( pixmapRect.size()); cache.fill( Qt::transparent); QPainter groovePainter(&cache); QLinearGradient gradient; if (horizontal) { gradient.setStart(pixmapRect.center().x(), pixmapRect.top()); gradient.setFinalStop(pixmapRect.center().x(), pixmapRect.bottom()); } else { gradient.setStart(pixmapRect.left(), pixmapRect.center().y()); gradient.setFinalStop(pixmapRect.right(), pixmapRect.center().y()); } QColor highlight = d->highlight( option->palette); QColor highlightedoutline = highlight.darker( 140); if (qGray(outline.rgb()) > qGray( highlightedoutline.rgb())) outline = highlightedoutline; groovePainter.setRenderHint(QPainter::Antialiasing, true); groovePainter.translate(0.5, 0.5); groovePainter.setPen(QPen(outline)); gradient.setColorAt(0, activeHighlight); gradient.setColorAt(1, activeHighlight.lighter( 130)); groovePainter.setBrush(gradient); groovePainter.drawRoundedRect(pixmapRect.adjusted(1, 1, -2, -2), 1, 1); groovePainter.setPen(d->innerContrastLine()); groovePainter.setBrush(Qt::NoBrush); groovePainter.drawRoundedRect(pixmapRect.adjusted(2, 2, -3, -3), 1, 1); groovePainter.end(); QPixmapCache::insert(groovePixmapName, cache); } if (horizontal) { if (slider->upsideDown) clipRect = QRect(handle.right(), groove.top(), groove.right() - handle.right(), groove.height()); else clipRect = QRect(groove.left(), groove.top(), handle.left(), groove.height()); } else { if (slider->upsideDown) clipRect = QRect(groove.left(), handle.bottom(), groove.width(), groove.height() - handle.bottom()); else clipRect = QRect(groove.left(), groove.top(), groove.width(), handle.top() - groove.top()); } painter->save(); painter->setClipRect(clipRect.adjusted(0, 0, 1, 1), Qt::IntersectClip); painter->drawPixmap(groove.topLeft(), cache); painter->restore(); } if (option->subControls & SC_SliderTickmarks) { painter->setPen(outline); int tickSize = proxy()->pixelMetric( PM_SliderTickmarkOffset, option, widget); int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); int interval = slider->tickInterval; if (interval <= 0) { interval = slider->singleStep; if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, available) - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, 0, available) < 3) interval = slider->pageStep; } if (interval <= 0) interval = 1; int v = slider->minimum; int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); while (v <= slider->maximum + 1) { if (v == slider->maximum + 1 && interval == 1) break; const int v_ = qMin(v, slider->maximum); int pos = sliderPositionFromValue(slider->minimum, slider->maximum, v_, (horizontal ? slider->rect.width() : slider->rect.height()) - len, slider->upsideDown) + len / 2; int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0); if (horizontal) { if (ticksAbove) { painter->drawLine(pos, slider->rect.top() + extra, pos, slider->rect.top() + tickSize); } if (ticksBelow) { painter->drawLine(pos, slider->rect.bottom() - extra, pos, slider->rect.bottom() - tickSize); } } else { if (ticksAbove) { painter->drawLine(slider->rect.left() + extra, pos, slider->rect.left() + tickSize, pos); } if (ticksBelow) { painter->drawLine(slider->rect.right() - extra, pos, slider->rect.right() - tickSize, pos); } } // in the case where maximum is max int int nextInterval = v + interval; if (nextInterval < v) break; v = nextInterval; } } // draw handle if ((option->subControls & SC_SliderHandle) ) { QString handlePixmapName = QStyleHelper::uniqueName("slider_handle"_L1, option, handle.size()); if (!QPixmapCache::find(handlePixmapName, &cache)) { cache = styleCachePixmap( handle.size()); cache.fill( Qt::transparent); QRect pixmapRect(0, 0, handle.width(), handle.height()); QPainter handlePainter(&cache); QRect gradRect = pixmapRect.adjusted(2, 2, -2, -2); // gradient fill QRect r = pixmapRect.adjusted(1, 1, -2, -2); QLinearGradient gradient = qt_fusion_gradient(gradRect, d->buttonColor(option->palette), horizontal ? TopDown : FromLeft); handlePainter.setRenderHint(QPainter::Antialiasing, true); handlePainter.translate(0.5, 0.5); handlePainter.setPen(Qt::NoPen); handlePainter.setBrush(QColor(0, 0, 0, 40)); handlePainter.drawRect(r.adjusted(-1, 2, 1, -2)); handlePainter.setPen(QPen(d->outline( option->palette))); if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) handlePainter.setPen(QPen(d->highlightedOutline( option->palette))); handlePainter.setBrush(gradient); handlePainter.drawRoundedRect(r, 2, 2); handlePainter.setBrush(Qt::NoBrush); handlePainter.setPen(d->innerContrastLine()); handlePainter.drawRoundedRect(r.adjusted(1, 1, -1, -1), 2, 2); QColor cornerAlpha = outline.darker( 120); cornerAlpha.setAlpha(80); //handle shadow handlePainter.setPen(shadowAlpha); handlePainter.drawLine(QPoint(r.left() + 2, r.bottom() + 1), QPoint(r.right() - 2, r.bottom() + 1)); handlePainter.drawLine(QPoint(r.right() + 1, r.bottom() - 3), QPoint(r.right() + 1, r.top() + 4)); handlePainter.drawLine(QPoint(r.right() - 1, r.bottom()), QPoint(r.right() + 1, r.bottom() - 2)); handlePainter.end(); QPixmapCache::insert(handlePixmapName, cache); } painter->drawPixmap(handle.topLeft(), cache); } painter->setBrush(oldBrush); painter->setPen(oldPen); } break; #endif // QT_CONFIG(slider)
-
Hi,
You can do it the same way the fusion style does but you have to tell qmake to use the
widget-private
module. Don't forget that using it will make your application highly dependent on the Qt version you use. -
@JoeCFD Do stylesheets support custom states that are based on some values of my custom slider or do i have to update the stylesheet at runtime?