1 #define ROUNDED_PIXEL    5
2 #define SHADOW_COLOR     palette().highlight().color()
3 #define SHADOW_SIZE      20
4 
5 #include "asemannativenotificationitem.h"
6 
7 #include <QPainterPath>
8 #include <QPaintEvent>
9 #include <QSize>
10 #include <QPainter>
11 #include <QGraphicsBlurEffect>
12 #include <QLabel>
13 #include <QPushButton>
14 #include <QToolButton>
15 #include <QHBoxLayout>
16 #include <QVBoxLayout>
17 #include <QDesktopWidget>
18 #include <QApplication>
19 #include <QTimer>
20 #include <QPixmap>
21 
22 class DialogBack: public QWidget
23 {
24 public:
DialogBack(QWidget * parent=0)25     DialogBack( QWidget *parent = 0 ): QWidget(parent){
26         effect = new QGraphicsBlurEffect(this);
27         effect->setBlurRadius(SHADOW_SIZE-10);
28         setGraphicsEffect(effect);
29         color = QColor(0,0,0,255);
30     }
31 
setColor(const QColor & color)32     void setColor( const QColor & color ){
33         DialogBack::color = color;
34         repaint();
35     }
36 
~DialogBack()37     ~DialogBack(){}
38 
39 protected:
paintEvent(QPaintEvent * e)40     void paintEvent(QPaintEvent *e){
41         QPainter painter(this);
42         painter.setRenderHint( QPainter::Antialiasing , true );
43         painter.fillRect( e->rect(), color );
44     }
45 
46 private:
47     QGraphicsBlurEffect *effect;
48     QColor color;
49 };
50 
51 class DialogScene: public QWidget
52 {
53 public:
DialogScene(QWidget * parent=0)54     DialogScene( QWidget *parent = 0 ): QWidget(parent){    }
~DialogScene()55     ~DialogScene(){}
56 
dialogPath(const QRect & rct,int padding) const57     QPainterPath dialogPath( const QRect & rct , int padding ) const
58     {
59         QPainterPath path;
60         path.setFillRule( Qt::WindingFill );
61 
62         path.moveTo( rct.width()/2 , padding );
63         path.lineTo( rct.width()-padding-ROUNDED_PIXEL , padding );
64         path.quadTo( rct.width()-padding , padding , rct.width()-padding , padding+ROUNDED_PIXEL );
65         path.lineTo( rct.width()-padding , rct.height()-padding-ROUNDED_PIXEL );
66         path.quadTo( rct.width()-padding , rct.height()-padding , rct.width()-padding-ROUNDED_PIXEL , rct.height()-padding );
67         path.lineTo( padding+ROUNDED_PIXEL , rct.height()-padding );
68         path.quadTo( padding , rct.height()-padding , padding , rct.height()-padding-ROUNDED_PIXEL );
69         path.lineTo( padding , padding+ROUNDED_PIXEL );
70         path.quadTo( padding , padding , padding+ROUNDED_PIXEL , padding );
71         path.lineTo( rct.width()/2 , padding );
72 
73         return path;
74     }
75 
76 protected:
paintEvent(QPaintEvent * e)77     void paintEvent(QPaintEvent *e){
78         Q_UNUSED(e)
79         QPainter painter(this);
80         painter.setRenderHint( QPainter::Antialiasing , true );
81         painter.fillPath( dialogPath(rect(),0), QColor(255,255,255) );
82     }
83 
84 private:
85     QGraphicsBlurEffect *effect;
86 };
87 
88 class AsemanNativeNotificationItemPrivate
89 {
90 public:
91     DialogBack *back;
92     DialogScene *scene;
93 
94     QColor shadow_color;
95 
96     QVBoxLayout *layout;
97     QHBoxLayout *body_layout;
98     QVBoxLayout *btns_layout;
99     QHBoxLayout *ttle_layout;
100 
101     QLabel *title_lbl;
102     QLabel *body_lbl;
103     QLabel *icon_lbl;
104 
105     QList<QPushButton*> buttons;
106     QHash<QPushButton*,QString> actions;
107 
108     QToolButton *close_btn;
109 };
110 
111 
AsemanNativeNotificationItem(QWidget * parent)112 AsemanNativeNotificationItem::AsemanNativeNotificationItem(QWidget *parent) :
113     QWidget(parent)
114 {
115     p = new AsemanNativeNotificationItemPrivate;
116     p->shadow_color = QColor( SHADOW_COLOR );
117 
118     p->back = new DialogBack(this);
119     p->back->setColor( p->shadow_color );
120 
121     p->scene = new DialogScene( this );
122 
123     p->title_lbl = new QLabel();
124     p->title_lbl->setAlignment(Qt::AlignCenter);
125     p->title_lbl->setFixedHeight(26);
126 
127     p->close_btn = new QToolButton();
128     p->close_btn->setText("X");
129     p->close_btn->setFixedSize(26, 26);
130     p->close_btn->setAutoRaise(true);
131 
132     p->ttle_layout = new QHBoxLayout();
133     p->ttle_layout->addWidget(p->title_lbl);
134     p->ttle_layout->addWidget(p->close_btn);
135     p->ttle_layout->setContentsMargins(0,0,0,0);
136     p->ttle_layout->setSpacing(1);
137 
138     p->icon_lbl = new QLabel();
139     p->icon_lbl->setFixedSize(64, 64);
140     p->icon_lbl->setScaledContents(true);
141 
142     p->body_lbl = new QLabel();
143     p->body_lbl->setWordWrap(true);
144 
145     p->btns_layout = new QVBoxLayout();
146     p->btns_layout->setContentsMargins(0,0,0,0);
147     p->btns_layout->setSpacing(1);
148 
149     p->body_layout = new QHBoxLayout();
150     p->body_layout->addWidget(p->icon_lbl);
151     p->body_layout->addWidget(p->body_lbl, 10000);
152     p->body_layout->addLayout(p->btns_layout);
153     p->body_layout->setContentsMargins(0,0,0,0);
154     p->body_layout->setSpacing(8);
155 
156     p->layout = new QVBoxLayout(this);
157     p->layout->addLayout(p->ttle_layout);
158     p->layout->addLayout(p->body_layout);
159     p->layout->setContentsMargins(SHADOW_SIZE+10,SHADOW_SIZE+8,SHADOW_SIZE+10,SHADOW_SIZE+8);
160     p->layout->setSpacing(1);
161 
162     setWindowFlags( Qt::Window | Qt::FramelessWindowHint | Qt::ToolTip );
163     setAttribute(Qt::WA_TranslucentBackground);
164     setAttribute(Qt::WA_NoSystemBackground);
165     setAttribute(Qt::WA_DeleteOnClose);
166     setMouseTracking( true );
167     setWindowOpacity(0.9);
168 
169     refreshSize();
170 
171     connect(p->close_btn, SIGNAL(clicked()), SLOT(close()) );
172 }
173 
setActions(const QStringList & actions)174 void AsemanNativeNotificationItem::setActions(const QStringList &actions)
175 {
176     for(int i=0; i<p->btns_layout->count(); i++)
177         delete p->btns_layout->takeAt(i);
178 
179     for(int i=1 ;i<actions.count(); i+=2)
180     {
181         const QString &action = actions.at(i-1);
182         const QString &text = actions.at(i);
183 
184         QPushButton *btn = new QPushButton();
185         btn->setText(text);
186 
187         p->actions.insert(btn, action);
188         p->buttons << btn;
189 
190         p->btns_layout->addWidget(btn);
191 
192         connect(btn, SIGNAL(clicked()), SLOT(buttonClicked()) );
193     }
194 
195     p->body_layout->addStretch();
196 }
197 
setTitle(const QString & title)198 void AsemanNativeNotificationItem::setTitle(const QString &title)
199 {
200     p->title_lbl->setText(title);
201 }
202 
setBody(const QString & body)203 void AsemanNativeNotificationItem::setBody(const QString &body)
204 {
205     p->body_lbl->setText(body.left(100) + "...");
206 }
207 
setIcon(const QString & icon)208 void AsemanNativeNotificationItem::setIcon(const QString &icon)
209 {
210     p->icon_lbl->setPixmap( QPixmap(icon) );
211 }
212 
setTimeOut(int timeOut)213 void AsemanNativeNotificationItem::setTimeOut(int timeOut)
214 {
215     if(timeOut == 0)
216         return;
217 
218     QTimer::singleShot(timeOut, this, SLOT(close()) );
219 }
220 
resizeEvent(QResizeEvent * e)221 void AsemanNativeNotificationItem::resizeEvent(QResizeEvent *e)
222 {
223     refreshSize();
224     QWidget::resizeEvent(e);
225 }
226 
mouseReleaseEvent(QMouseEvent * e)227 void AsemanNativeNotificationItem::mouseReleaseEvent(QMouseEvent *e)
228 {
229     Q_UNUSED(e)
230     close();
231 }
232 
refreshSize()233 void AsemanNativeNotificationItem::refreshSize()
234 {
235     QRect rect( SHADOW_SIZE, SHADOW_SIZE, width()-2*SHADOW_SIZE, height()-2*SHADOW_SIZE );
236 
237     const QRect &scr = QApplication::desktop()->availableGeometry();
238 
239     p->back->setGeometry( rect );
240     p->scene->setGeometry( rect );
241 
242     move(scr.x()+scr.width()-width()+SHADOW_SIZE*0.7, scr.y()+scr.height()-height()+SHADOW_SIZE*0.7);
243 }
244 
setRaised()245 void AsemanNativeNotificationItem::setRaised()
246 {
247     raise();
248 }
249 
buttonClicked()250 void AsemanNativeNotificationItem::buttonClicked()
251 {
252     QPushButton *btn = static_cast<QPushButton*>(sender());
253     if(!btn)
254         return;
255 
256     const QString &action = p->actions.value(btn);
257     emit actionTriggered(action);
258 }
259 
~AsemanNativeNotificationItem()260 AsemanNativeNotificationItem::~AsemanNativeNotificationItem()
261 {
262     delete p;
263 }
264 
265