redeyerabbit

博客园 首页 新随笔 联系 订阅 管理

前面使用Qt 样式表实现滚动条,在实际工作中,发现存在一些瑕疵,例如如果在主窗口中绘制背景,则有可能给滚动条染色,还有如果想实现特殊的效果,则必须使用自定义风格,即从QStyle的子类派生出新的类型。以下从QProxyStyl派生出新的风格来实现自定义滚动(Qt4、Qt5均存在QCommonStyle、QProxyStyle,其余的风格在Qt5中已经不存在,不过原理一致)。

首先QScrollbar重绘时直接调用

drawComplexControl( ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget ) const

由此开始我们的自定绘制滚动条:

void MyStyle::drawComplexControl( ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget ) const
{
    switch (control)
    {
    case CC_ScrollBar:
        drawScrollbar(control, option, painter, widget);
        return;
    default:
        break;
    }
    __super::drawComplexControl(control, option, painter, widget);
}

绘制代码的实现(模拟QQ)

void MyStyle::drawScrollbar( ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget ) const
{
    //QQ中滚动条的效果是:如果不在滚动条上,默认上下箭头不显示,AddPage, SubPage也不显示。
    //如果在滚动条上,如果不在箭头所在的区域,则显示normal,否则显示hot,按下显示down。
    //这里按这个标准实现。
    const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option);
    if(!scrollbar) return;
    bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
    if(maxedOut) return;

    painter->save();
    painter->setRenderHint(QPainter::Antialiasing);
    QRect r = option->rect;

    const State state = option->state;
    const SubControls sub = option->subControls;

    State flags = option->state;
    if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
        flags |= State_MouseOver;

    QRect drawRect;

    if (maxedOut)
        flags &= ~State_Enabled;

    const bool pressed = flags & State_Sunken;
    const bool mouseOver = flags & State_MouseOver;
    const bool isHorz = flags & State_Horizontal;
    const bool isRTL  = option->direction == Qt::RightToLeft;

    QColor bkFillColor = Qt::white;
    QColor bkSliderNormal = Qt::lightGray;
    QColor bkSliderHover = Qt::gray;
    QColor bkSliderPress = Qt::darkGray;

    //清除所有子控件背景
    //上下(左右)箭头可能和沟槽的背景不一样
    drawRect= subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
    painter->fillRect(drawRect, bkFillColor);

    drawRect= subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
    painter->fillRect(drawRect, bkFillColor);

    //沟槽的颜色
    drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget);
    painter->fillRect(drawRect, Qt::green);
    //在mouse over状态下,才绘制上下左右箭头。
    //scrollbar->activeSubControls用于指定当前鼠标所在的子控件
    const QStyle::SubControls sc = scrollbar->activeSubControls;
    if(!mouseOver) 
    {
        //只绘制Slider,其余清除
        drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);

        QPainterPath painterPath;
        painterPath.addRoundedRect(drawRect.left(), drawRect.top(), drawRect.width(), drawRect.height(), 4, 4);
        painter->fillPath(painterPath, bkSliderNormal);
    }
    else
    {
        //合并绘制AddPage SubPage
        drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget);
        painter->fillRect(drawRect, QColor(192, 192, 192, 127));

        if (sub & SC_ScrollBarAddLine) //画加号,应该区分水平和垂直
        {
            drawRect= subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
            QPixmap pixmap;
            if(isHorz)
            {
                if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowright_down.png");
                else if(sc & SC_ScrollBarAddLine) pixmap.load(":/ScrollBar/scrollbar_arrowright_highlight.png");
                else pixmap.load(":/ScrollBar/scrollbar_arrowright_normal.png");
            }
            else
            {
                if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowdown_down.png");
                else if(sc & SC_ScrollBarAddLine) pixmap.load(":/ScrollBar/scrollbar_arrowdown_highlight.png");
                else pixmap.load(":/ScrollBar/scrollbar_arrowdown_normal.png");
            }

            painter->drawPixmap(drawRect, pixmap);
        }
        if (sub & SC_ScrollBarSubLine) 
        {
            drawRect= subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
            QPixmap pixmap;
            
            if(isHorz)
            {
                if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowleft_down.png");
                else if(sc & SC_ScrollBarSubLine) pixmap.load(":/ScrollBar/scrollbar_arrowleft_highlight.png");
                else pixmap.load(":/ScrollBar/scrollbar_arrowleft_normal.png");
            }
            else
            {
                if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowup_down.png");
                else if(sc & SC_ScrollBarSubLine) pixmap.load(":/ScrollBar/scrollbar_arrowup_highlight.png");
                else pixmap.load(":/ScrollBar/scrollbar_arrowup_normal.png");
            }

            painter->drawPixmap(drawRect, pixmap);
        }
        if (sub & SC_ScrollBarSlider) 
        {
            drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);

            QPainterPath painterPath;
            painterPath.addRoundedRect(drawRect.left(), drawRect.top(), drawRect.width(), drawRect.height(), 4, 4);
            if(sc & SC_ScrollBarSlider)
            {
                painter->fillPath(painterPath, bkSliderPress);
            }
            else
            {
                painter->fillPath(painterPath, bkSliderHover);
            }
        }

    }
    painter->restore();
}

效果如下:

 

左为鼠标不在滚动条区域,右边为鼠标在滚动条区域

这里使用绿色做沟槽的背景,方便调试。

 

posted on 2013-10-23 16:55  redeyerabbit  阅读(2624)  评论(0编辑  收藏  举报