接着干!
以弄好一个pushbutton为目标,使劲的去看代码并抄一抄流程,适当进行改动能深入理解该地方的用处!
还是先整合好框架然后留空
通过查阅资料观察流程,得到基本原理。每个控件有若干个子元素构成,而插件通过修改编辑这些子元素打到修改控件外观的效果。
所以步骤是一样的,代码和网上说的也差不多。基本都是编写好父类的虚函数就行了
ok我们的主要编辑文件mystyle这个类,开始对它动手动脚。
虚函数polish()
有三个:
- virtual void polish(QWidget *widget);
- virtual void polish(QPalette &palette);
- virtual void unpolish(QWidget *widget);
第一个设置一下悬浮,重载一下事件过滤(虽然也没写新的),再调用一下父类:
void MyStyle::polish(QWidget *widget)
{
if (!widget)
return;
if (qobject_cast<QPushButton *>(widget))
widget->setAttribute(Qt::WA_Hover);
if (!widget->parent() || !qobject_cast<QWidget *>(widget->parent()) || qobject_cast<QDialog *>(widget) || qobject_cast<QMainWindow *>(widget)) {
addEventFilter(widget);
}
// base class polishing
QCommonStyle::polish(widget);
}
第二个看名字就知道是设置调色版,先随意吧,太长了,大概就那些玩意儿,直接搬过来就是:
void MyStyle::polish(QPalette &palette)
{
// Colors defined in GTK adwaita style in _colors.scss
QColor base_color = QColor("#2c9dfc");
QColor text_color = QColor("black");
QColor bg_color = QColor("#f6f5f4");
QColor fg_color = QColor("#2e3436");
QColor selected_bg_color = QColor("#3584e4");
QColor selected_fg_color = QColor("white");
QColor osd_text_color = QColor("white");
QColor osd_bg_color = QColor("black");
QColor shadow = _helper->transparentize(bg_color, 0.9);
QColor backdrop_fg_color = _helper->mix(fg_color, bg_color);
QColor backdrop_base_color = _helper->darken(base_color, 0.01);
QColor backdrop_selected_fg_color = backdrop_base_color;
// This is the color we use as initial color for the gradient in normal state
// Defined in _drawing.scss button(normal)
QColor button_base_color = _helper->darken(QColor("#ffffff"), 0.04);
QColor link_color = _helper->darken(selected_bg_color, 0.1);
QColor link_visited_color = _helper->darken(selected_bg_color, 0.2);
palette.setColor(QPalette::All, QPalette::Window, bg_color);
palette.setColor(QPalette::All, QPalette::WindowText, fg_color);
palette.setColor(QPalette::All, QPalette::Base, base_color);
palette.setColor(QPalette::All, QPalette::AlternateBase, base_color);
...
...
...
}
第三个析构,也是直接回归父类就行:
void MyStyle::unpolish(QWidget *widget)
{
QCommonStyle::unpolish(widget);
}
pixelMetric、subElementRect、styleHint、subControlRect、sizeFromContents、hitTestComplexControl
这几个虚函数都分别微调一些控件子元素的外观矩形大小,这些没有太大的改动,也先直接跳过返回父类即可:
int MyStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
{
return QCommonStyle::pixelMetric(metric, option, widget);
}
QRect MyStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
{
return QCommonStyle::subElementRect(element, option, widget);
}
int MyStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const
{
return QCommonStyle::styleHint(hint, option, widget, returnData);
}
QRect MyStyle::subControlRect(ComplexControl element, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const
{
return QCommonStyle::subControlRect(element, option, subControl, widget);
}
QSize MyStyle::sizeFromContents(ContentsType element, const QStyleOption *option, const QSize &size, const QWidget *widget) const
{
return QCommonStyle::sizeFromContents(element, option, size, widget);
}
QStyle::SubControl MyStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, const QPoint &point, const QWidget *widget) const
{
return QCommonStyle::hitTestComplexControl(control, option, point, widget);
}
drawPrimitive
绘制PE子元素,其实暂时也不知道怎么查资料可以得到各个控件的子元素有什么,笨拙的通过打印看按钮有什么子元素可太秀了,好的通过打印看出按钮有两个PE子元素:PE_PanelButtonCommand、PE_FrameFocusRect。子元素可在官方文档中查看具体代表是什么,比如第一个官方的描述是“Button used to initiate an action”,第二个其实不用看文档也偶尔猜到是绘制聚焦时的矩形样式;
分别列出来:
void MyStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
StylePrimitive fcn(nullptr);
switch (element) {
case PE_PanelButtonCommand:
fcn = &MyStyle::drawPanelButtonCommandPrimitive;
break;
case PE_FrameFocusRect:
fcn = &MyStyle::drawFrameFocusRectPrimitive;
break;
default:
break;
}
painter->save();
// call function if implemented
if (!(fcn && (this->*fcn)(option, painter, widget))) {
QCommonStyle::drawPrimitive(element, option, painter, widget);
}
painter->restore();
}
其中第一句这个算是泛型指针?写着方便,也是高级写法,所指的函数参数都一样,定义是这样的:
using StylePrimitive = bool(MyStyle::*)(const QStyleOption *option, QPainter *painter, const QWidget *widget) const;
具体绘画函数draw…先留空,这个是真的画画的时候了。
drawComplexControl、drawItemText
前面这个定制CC子元素,看了下button没有CC子元素,也没有特殊的itemtext需求,也是直接返回父类:
void MyStyle::drawComplexControl(ComplexControl element, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const
{
QCommonStyle::drawComplexControl(element, option, painter, widget);
}
void MyStyle::drawItemText(QPainter *painter, const QRect &rect, int flags, const QPalette &palette, bool enabled,const QString &text, QPalette::ColorRole textRole) const
{
return QCommonStyle::drawItemText(painter, rect, flags, palette, enabled, text, textRole);
}
drawControl
定制CE子元素,button的CE有CE_PushButtonBevel、CE_PushButtonLabel。具体是什么就不再说了文档都有,也通过浅而易懂的命名知道意思了,格式一样:
void MyStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
StyleControl fcn(nullptr);
switch (element) {
case CE_PushButtonBevel:
fcn = &MyStyle::drawPanelButtonCommandPrimitive;
break;
case CE_PushButtonLabel:
fcn = &MyStyle::drawPushButtonLabelControl;
break;
default:
break;
}
painter->save();
// call function if implemented
if (!(fcn && (this->*fcn)(option, painter, widget))) {
QCommonStyle::drawControl(element, option, painter, widget);
}
painter->restore();
}
开始画画!
OK主要的虚函数都写好了,剩下画画的,仔细看每个虚函数给的参数,第一个是option包含着控件的属性,第二个画笔,第三个就是它自己,用这些参数足以开始画画了!
drawFrameFocusRectPrimitive
这个在adwaita源码中很明显的,它把聚焦矩形缩小一点,然后再用虚线画出这个矩形,效果是这样:
代码:
bool MyStyle::drawFrameFocusRectPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
const State &state(option->state);
QRectF rect(QRectF(option->rect).adjusted(0, 0, -1, -1));
const QPalette &palette(option->palette);
if (rect.width() < 10)
return true;
QColor outlineColor(Helper::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.35));
QPen pen(outlineColor, 1);
pen.setStyle(Qt::CustomDashLine);
pen.setDashPattern(QVector<qreal>() << 2 << 1);
painter->setRenderHint(QPainter::Antialiasing, false);
painter->setPen(pen);
painter->drawRoundedRect(rect, 2, 2);
return true;
}
也是显而易懂的,得到矩形后缩小,设置颜色,设置画笔,画矩形。
这个不太喜欢,直接去掉算了。
其中这个helper是用作渲染的类,主要作用是调色和封装一些画矩形的函数,其实这里可以随便上一种颜色即可,这个类就不详细说明了。
drawPanelButtonCommandPrimitive
这个是相当于初始化按钮绘制的函数了,代码:
bool MyStyle::drawPanelButtonCommandPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
// cast option and check
const QStyleOptionButton *buttonOption(qstyleoption_cast< const QStyleOptionButton * >(option));
if (!buttonOption)
return true;
// rect and palette
const QRect &rect(option->rect);
// button state
const State &state(option->state);
bool enabled(state & State_Enabled);
bool windowActive(state & State_Active);
bool mouseOver((state & State_Active) && enabled && (state & State_MouseOver));
bool hasFocus((enabled && (state & State_HasFocus)) && !(widget && widget->focusProxy()));
bool sunken(state & (State_On | State_Sunken));
bool flat(buttonOption->features & QStyleOptionButton::Flat);
if (flat) {
// define colors and render
const QPalette &palette(option->palette);
QColor color(_helper->toolButtonColor(palette, mouseOver, hasFocus, sunken));
_helper->renderToolButtonFrame(painter, rect, color, sunken);
} else {
// update button color from palette in case button is default
QPalette palette(option->palette);
if (enabled && buttonOption->features & QStyleOptionButton::DefaultButton) {
QColor button(palette.color(QPalette::Button));
QColor base(palette.color(QPalette::Base));
palette.setColor(QPalette::Button, Helper::mix(button, base, 0.7));
}
QColor shadow(palette.color(QPalette::Shadow));
QColor outline(_helper->buttonOutlineColor(palette, mouseOver, hasFocus));
QColor background(_helper->buttonBackgroundColor(palette, mouseOver, hasFocus, sunken));
// render
_helper->renderButtonFrame(painter, rect, background, outline, shadow, hasFocus, sunken, mouseOver, enabled && windowActive);
}
return true;
}
到这里发现绘制的流程基本也一致,显示获取rect要操作的矩形,获取各种状态参数,然后根据这些数据绘制即可,这是通过获取这个按钮的是否透明、点击、鼠标悬浮、使能失能等参数去画,这个_helper也只是封装了这个过程,也不过是判断状态state作出相应绘制而已。
drawPushButtonLabelControl
这里看了一下,基本也就判断下状态,其中有个判断是否有文字并且点击时字体矩形会稍微网右下角倾斜的效果,这个就实现了比较动态的感觉。
if (hasText && textRect.isValid()) {
if (enabled && !sunken && !mouseOver && !flat) {
drawItemText(painter, textRect.adjusted(0, 1, 0, 1), textFlags, palette, false, buttonOption->text, QPalette::Light);
}
drawItemText(painter, textRect, textFlags, palette, enabled, buttonOption->text, textRole);
}
看看成果!
还记得demo吗,记得上面有一句QApplication::setStyle()吗?
现在把它弄成QApplication::setStyle(“mystyle”);运行看看: