2017-04-27 18:26:05 +00:00
// SPDX-License-Identifier: LGPL-2.1+
2013-05-18 00:58:49 +00:00
/* This file is part of the KDE libraries
*
* Copyright ( c ) 2011 Aurélien Gâteau < agateau @ kde . org >
2015-06-04 01:08:57 +00:00
* Copyright ( c ) 2014 Dominik Haumann < dhaumann @ kde . org >
2013-05-18 00:58:49 +00:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*/
# include "kmessagewidget.h"
2015-06-04 01:08:57 +00:00
# include <QAction>
# include <QEvent>
2013-05-18 00:58:49 +00:00
# include <QGridLayout>
2015-06-04 01:08:57 +00:00
# include <QHBoxLayout>
2013-05-18 00:58:49 +00:00
# include <QLabel>
# include <QPainter>
# include <QShowEvent>
# include <QTimeLine>
# include <QToolButton>
# include <QStyle>
2017-03-25 08:23:17 +00:00
# include <cmath>
2015-06-04 01:08:57 +00:00
//---------------------------------------------------------------------
// KMessageWidgetPrivate
//---------------------------------------------------------------------
class KMessageWidgetPrivate
{
public :
void init ( KMessageWidget * ) ;
KMessageWidget * q ;
QFrame * content ;
QLabel * iconLabel ;
QLabel * textLabel ;
QToolButton * closeButton ;
QTimeLine * timeLine ;
QIcon icon ;
KMessageWidget : : MessageType messageType ;
bool wordWrap ;
QList < QToolButton * > buttons ;
QPixmap contentSnapShot ;
void createLayout ( ) ;
void updateSnapShot ( ) ;
void updateLayout ( ) ;
void slotTimeLineChanged ( qreal ) ;
void slotTimeLineFinished ( ) ;
int bestContentHeight ( ) const ;
} ;
2013-05-18 00:58:49 +00:00
2014-02-28 04:09:57 +00:00
void KMessageWidgetPrivate : : init ( KMessageWidget * q_ptr )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
q = q_ptr ;
q - > setSizePolicy ( QSizePolicy : : Minimum , QSizePolicy : : Fixed ) ;
timeLine = new QTimeLine ( 500 , q ) ;
QObject : : connect ( timeLine , SIGNAL ( valueChanged ( qreal ) ) , q , SLOT ( slotTimeLineChanged ( qreal ) ) ) ;
QObject : : connect ( timeLine , SIGNAL ( finished ( ) ) , q , SLOT ( slotTimeLineFinished ( ) ) ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
content = new QFrame ( q ) ;
content - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Fixed ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
wordWrap = false ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
iconLabel = new QLabel ( content ) ;
iconLabel - > setSizePolicy ( QSizePolicy : : Fixed , QSizePolicy : : Fixed ) ;
iconLabel - > hide ( ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
textLabel = new QLabel ( content ) ;
textLabel - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Fixed ) ;
textLabel - > setTextInteractionFlags ( Qt : : TextBrowserInteraction ) ;
QObject : : connect ( textLabel , SIGNAL ( linkActivated ( QString ) ) , q , SIGNAL ( linkActivated ( QString ) ) ) ;
QObject : : connect ( textLabel , SIGNAL ( linkHovered ( QString ) ) , q , SIGNAL ( linkHovered ( QString ) ) ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
QAction * closeAction = new QAction ( q ) ;
closeAction - > setText ( KMessageWidget : : tr ( " &Close " ) ) ;
closeAction - > setToolTip ( KMessageWidget : : tr ( " Close message " ) ) ;
closeAction - > setIcon ( q - > style ( ) - > standardIcon ( QStyle : : SP_DialogCloseButton ) ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
QObject : : connect ( closeAction , SIGNAL ( triggered ( bool ) ) , q , SLOT ( animatedHide ( ) ) ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
closeButton = new QToolButton ( content ) ;
closeButton - > setAutoRaise ( true ) ;
closeButton - > setDefaultAction ( closeAction ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
q - > setMessageType ( KMessageWidget : : Information ) ;
2013-05-18 00:58:49 +00:00
}
void KMessageWidgetPrivate : : createLayout ( )
{
2015-06-04 01:08:57 +00:00
delete content - > layout ( ) ;
content - > resize ( q - > size ( ) ) ;
qDeleteAll ( buttons ) ;
buttons . clear ( ) ;
2024-03-16 15:50:43 +00:00
for ( QAction * action : q - > actions ( ) ) {
2015-06-04 01:08:57 +00:00
QToolButton * button = new QToolButton ( content ) ;
button - > setDefaultAction ( action ) ;
button - > setToolButtonStyle ( Qt : : ToolButtonTextBesideIcon ) ;
buttons . append ( button ) ;
}
// AutoRaise reduces visual clutter, but we don't want to turn it on if
// there are other buttons, otherwise the close button will look different
// from the others.
closeButton - > setAutoRaise ( buttons . isEmpty ( ) ) ;
if ( wordWrap ) {
QGridLayout * layout = new QGridLayout ( content ) ;
// Set alignment to make sure icon does not move down if text wraps
layout - > addWidget ( iconLabel , 0 , 0 , 1 , 1 , Qt : : AlignHCenter | Qt : : AlignTop ) ;
layout - > addWidget ( textLabel , 0 , 1 ) ;
QHBoxLayout * buttonLayout = new QHBoxLayout ;
buttonLayout - > addStretch ( ) ;
2024-03-16 15:50:43 +00:00
for ( QToolButton * button : buttons ) {
2015-06-04 01:08:57 +00:00
// For some reason, calling show() is necessary if wordwrap is true,
// otherwise the buttons do not show up. It is not needed if
// wordwrap is false.
button - > show ( ) ;
buttonLayout - > addWidget ( button ) ;
}
buttonLayout - > addWidget ( closeButton ) ;
layout - > addItem ( buttonLayout , 1 , 0 , 1 , 2 ) ;
} else {
QHBoxLayout * layout = new QHBoxLayout ( content ) ;
layout - > addWidget ( iconLabel ) ;
layout - > addWidget ( textLabel ) ;
2024-03-16 15:50:43 +00:00
for ( QToolButton * button : buttons )
2015-06-04 01:08:57 +00:00
layout - > addWidget ( button ) ;
layout - > addWidget ( closeButton ) ;
} ;
if ( q - > isVisible ( ) ) {
q - > setFixedHeight ( content - > sizeHint ( ) . height ( ) ) ;
}
q - > updateGeometry ( ) ;
2013-05-18 00:58:49 +00:00
}
void KMessageWidgetPrivate : : updateLayout ( )
{
2015-06-04 01:08:57 +00:00
if ( content - > layout ( ) ) {
createLayout ( ) ;
}
2013-05-18 00:58:49 +00:00
}
void KMessageWidgetPrivate : : updateSnapShot ( )
{
2015-06-04 01:08:57 +00:00
// Attention: updateSnapShot calls QWidget::render(), which causes the whole
// window layouts to be activated. Calling this method from resizeEvent()
// can lead to infinite recursion, see:
// https://bugs.kde.org/show_bug.cgi?id=311336
contentSnapShot = QPixmap ( content - > size ( ) * q - > devicePixelRatio ( ) ) ;
contentSnapShot . setDevicePixelRatio ( q - > devicePixelRatio ( ) ) ;
contentSnapShot . fill ( Qt : : transparent ) ;
content - > render ( & contentSnapShot , QPoint ( ) , QRegion ( ) , QWidget : : DrawChildren ) ;
2013-05-18 00:58:49 +00:00
}
void KMessageWidgetPrivate : : slotTimeLineChanged ( qreal value )
{
2017-03-23 01:13:49 +00:00
q - > setFixedHeight ( lrint ( qMin ( value * 2 , qreal ( 1.0 ) ) * content - > height ( ) ) ) ;
q - > update ( ) ;
2013-05-18 00:58:49 +00:00
}
void KMessageWidgetPrivate : : slotTimeLineFinished ( )
{
2015-06-04 01:08:57 +00:00
if ( timeLine - > direction ( ) = = QTimeLine : : Forward ) {
// Show
// We set the whole geometry here, because it may be wrong if a
// KMessageWidget is shown right when the toplevel window is created.
content - > setGeometry ( 0 , 0 , q - > width ( ) , bestContentHeight ( ) ) ;
// notify about finished animation
emit q - > showAnimationFinished ( ) ;
} else {
// hide and notify about finished animation
q - > hide ( ) ;
emit q - > hideAnimationFinished ( ) ;
}
2013-05-18 00:58:49 +00:00
}
int KMessageWidgetPrivate : : bestContentHeight ( ) const
{
2015-06-04 01:08:57 +00:00
int height = content - > heightForWidth ( q - > width ( ) ) ;
if ( height = = - 1 ) {
height = content - > sizeHint ( ) . height ( ) ;
}
return height ;
2013-05-18 00:58:49 +00:00
}
//---------------------------------------------------------------------
// KMessageWidget
//---------------------------------------------------------------------
2015-06-04 01:08:57 +00:00
KMessageWidget : : KMessageWidget ( QWidget * parent )
: QFrame ( parent )
, d ( new KMessageWidgetPrivate )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
d - > init ( this ) ;
2013-05-18 00:58:49 +00:00
}
2015-06-04 01:08:57 +00:00
KMessageWidget : : KMessageWidget ( const QString & text , QWidget * parent )
: QFrame ( parent )
, d ( new KMessageWidgetPrivate )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
d - > init ( this ) ;
setText ( text ) ;
2013-05-18 00:58:49 +00:00
}
KMessageWidget : : ~ KMessageWidget ( )
{
2015-06-04 01:08:57 +00:00
delete d ;
2013-05-18 00:58:49 +00:00
}
QString KMessageWidget : : text ( ) const
{
2015-06-04 01:08:57 +00:00
return d - > textLabel - > text ( ) ;
2013-05-18 00:58:49 +00:00
}
2014-02-28 04:09:57 +00:00
void KMessageWidget : : setText ( const QString & text )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
d - > textLabel - > setText ( text ) ;
updateGeometry ( ) ;
2013-05-18 00:58:49 +00:00
}
2015-06-04 01:08:57 +00:00
KMessageWidget : : MessageType KMessageWidget : : messageType ( ) const
2014-11-17 01:28:48 +00:00
{
2015-06-04 01:08:57 +00:00
return d - > messageType ;
2014-11-17 01:28:48 +00:00
}
2015-06-04 01:08:57 +00:00
static QColor darkShade ( QColor c )
2013-05-18 00:58:49 +00:00
{
2022-02-15 19:23:27 +00:00
double contrast = 0.7 ; // taken from kcolorscheme for the dark shade
2015-06-04 01:08:57 +00:00
2022-02-15 19:23:27 +00:00
double darkAmount ;
2015-06-04 01:08:57 +00:00
if ( c . lightnessF ( ) < 0.006 ) { /* too dark */
darkAmount = 0.02 + 0.40 * contrast ;
} else if ( c . lightnessF ( ) > 0.93 ) { /* too bright */
darkAmount = - 0.06 - 0.60 * contrast ;
} else {
darkAmount = ( - c . lightnessF ( ) ) * ( 0.55 + contrast * 0.35 ) ;
}
2022-02-15 19:23:27 +00:00
double v = c . lightnessF ( ) + darkAmount ;
2015-06-04 01:08:57 +00:00
v = v > 0.0 ? ( v < 1.0 ? v : 1.0 ) : 0.0 ;
2022-02-15 19:23:27 +00:00
c . setHsvF ( c . hslHueF ( ) , c . hslSaturationF ( ) , ( float ) v ) ;
2015-06-04 01:08:57 +00:00
return c ;
2013-05-18 00:58:49 +00:00
}
void KMessageWidget : : setMessageType ( KMessageWidget : : MessageType type )
{
2015-06-04 01:08:57 +00:00
d - > messageType = type ;
QColor bg0 , bg1 , bg2 , border , fg ;
switch ( type ) {
case Positive :
bg1 . setRgb ( 0 , 110 , 40 ) ; // values taken from kcolorscheme.cpp (Positive)
break ;
case Information :
bg1 = palette ( ) . highlight ( ) . color ( ) ;
break ;
case Warning :
bg1 . setRgb ( 176 , 128 , 0 ) ; // values taken from kcolorscheme.cpp (Neutral)
break ;
case Error :
bg1 . setRgb ( 191 , 3 , 3 ) ; // values taken from kcolorscheme.cpp (Negative)
break ;
}
// Colors
fg = palette ( ) . highlightedText ( ) . color ( ) ;
bg0 = bg1 . lighter ( 110 ) ;
bg2 = bg1 . darker ( 110 ) ;
border = darkShade ( bg1 ) ;
d - > content - > setStyleSheet (
QString ( QLatin1String ( " .QFrame { "
" background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, "
" stop: 0 %1, "
" stop: 0.1 %2, "
" stop: 1.0 %3); "
" border-radius: 5px; "
" border: 1px solid %4; "
" margin: %5px; "
" } "
" .QLabel { color: %6; } "
) )
. arg ( bg0 . name ( ) )
. arg ( bg1 . name ( ) )
. arg ( bg2 . name ( ) )
. arg ( border . name ( ) )
// DefaultFrameWidth returns the size of the external margin + border width. We know our border is 1px, so we subtract this from the frame normal QStyle FrameWidth to get our margin
. arg ( style ( ) - > pixelMetric ( QStyle : : PM_DefaultFrameWidth , 0 , this ) - 1 )
. arg ( fg . name ( ) )
) ;
2013-05-18 00:58:49 +00:00
}
QSize KMessageWidget : : sizeHint ( ) const
{
2015-06-04 01:08:57 +00:00
ensurePolished ( ) ;
return d - > content - > sizeHint ( ) ;
2013-05-18 00:58:49 +00:00
}
QSize KMessageWidget : : minimumSizeHint ( ) const
{
2015-06-04 01:08:57 +00:00
ensurePolished ( ) ;
return d - > content - > minimumSizeHint ( ) ;
2013-05-18 00:58:49 +00:00
}
2014-02-28 04:09:57 +00:00
bool KMessageWidget : : event ( QEvent * event )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
if ( event - > type ( ) = = QEvent : : Polish & & ! d - > content - > layout ( ) ) {
d - > createLayout ( ) ;
}
return QFrame : : event ( event ) ;
2013-05-18 00:58:49 +00:00
}
2014-02-28 04:09:57 +00:00
void KMessageWidget : : resizeEvent ( QResizeEvent * event )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
QFrame : : resizeEvent ( event ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
if ( d - > timeLine - > state ( ) = = QTimeLine : : NotRunning ) {
d - > content - > resize ( width ( ) , d - > bestContentHeight ( ) ) ;
}
2013-05-18 00:58:49 +00:00
}
int KMessageWidget : : heightForWidth ( int width ) const
{
2015-06-04 01:08:57 +00:00
ensurePolished ( ) ;
return d - > content - > heightForWidth ( width ) ;
2013-05-18 00:58:49 +00:00
}
2014-02-28 04:09:57 +00:00
void KMessageWidget : : paintEvent ( QPaintEvent * event )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
QFrame : : paintEvent ( event ) ;
if ( d - > timeLine - > state ( ) = = QTimeLine : : Running ) {
QPainter painter ( this ) ;
painter . setOpacity ( d - > timeLine - > currentValue ( ) * d - > timeLine - > currentValue ( ) ) ;
painter . drawPixmap ( 0 , 0 , d - > contentSnapShot ) ;
}
2013-05-18 00:58:49 +00:00
}
bool KMessageWidget : : wordWrap ( ) const
{
2015-06-04 01:08:57 +00:00
return d - > wordWrap ;
2013-05-18 00:58:49 +00:00
}
void KMessageWidget : : setWordWrap ( bool wordWrap )
{
2015-06-04 01:08:57 +00:00
d - > wordWrap = wordWrap ;
d - > textLabel - > setWordWrap ( wordWrap ) ;
QSizePolicy policy = sizePolicy ( ) ;
policy . setHeightForWidth ( wordWrap ) ;
setSizePolicy ( policy ) ;
d - > updateLayout ( ) ;
// Without this, when user does wordWrap -> !wordWrap -> wordWrap, a minimum
// height is set, causing the widget to be too high.
// Mostly visible in test programs.
if ( wordWrap ) {
setMinimumHeight ( 0 ) ;
}
2013-05-18 00:58:49 +00:00
}
bool KMessageWidget : : isCloseButtonVisible ( ) const
{
2015-06-04 01:08:57 +00:00
return d - > closeButton - > isVisible ( ) ;
2013-05-18 00:58:49 +00:00
}
void KMessageWidget : : setCloseButtonVisible ( bool show )
{
2015-06-04 01:08:57 +00:00
d - > closeButton - > setVisible ( show ) ;
updateGeometry ( ) ;
2013-05-18 00:58:49 +00:00
}
2014-02-28 04:09:57 +00:00
void KMessageWidget : : addAction ( QAction * action )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
QFrame : : addAction ( action ) ;
d - > updateLayout ( ) ;
2013-05-18 00:58:49 +00:00
}
2014-02-28 04:09:57 +00:00
void KMessageWidget : : removeAction ( QAction * action )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
QFrame : : removeAction ( action ) ;
d - > updateLayout ( ) ;
2013-05-18 00:58:49 +00:00
}
void KMessageWidget : : animatedShow ( )
{
2015-06-04 01:08:57 +00:00
if ( ! style ( ) - > styleHint ( QStyle : : SH_Widget_Animate , 0 , this ) ) {
show ( ) ;
emit showAnimationFinished ( ) ;
return ;
}
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
if ( isVisible ( ) ) {
return ;
}
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
QFrame : : show ( ) ;
setFixedHeight ( 0 ) ;
int wantedHeight = d - > bestContentHeight ( ) ;
d - > content - > setGeometry ( 0 , - wantedHeight , width ( ) , wantedHeight ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
d - > updateSnapShot ( ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
d - > timeLine - > setDirection ( QTimeLine : : Forward ) ;
if ( d - > timeLine - > state ( ) = = QTimeLine : : NotRunning ) {
d - > timeLine - > start ( ) ;
}
2013-05-18 00:58:49 +00:00
}
void KMessageWidget : : animatedHide ( )
{
2015-06-04 01:08:57 +00:00
if ( ! style ( ) - > styleHint ( QStyle : : SH_Widget_Animate , 0 , this ) ) {
hide ( ) ;
emit hideAnimationFinished ( ) ;
return ;
}
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
if ( ! isVisible ( ) ) {
2015-07-16 17:53:24 +00:00
hide ( ) ;
2015-06-04 01:08:57 +00:00
return ;
}
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
d - > content - > move ( 0 , - d - > content - > height ( ) ) ;
d - > updateSnapShot ( ) ;
2013-05-18 00:58:49 +00:00
2015-06-04 01:08:57 +00:00
d - > timeLine - > setDirection ( QTimeLine : : Backward ) ;
if ( d - > timeLine - > state ( ) = = QTimeLine : : NotRunning ) {
d - > timeLine - > start ( ) ;
}
}
bool KMessageWidget : : isHideAnimationRunning ( ) const
{
return ( d - > timeLine - > direction ( ) = = QTimeLine : : Backward )
& & ( d - > timeLine - > state ( ) = = QTimeLine : : Running ) ;
}
bool KMessageWidget : : isShowAnimationRunning ( ) const
{
return ( d - > timeLine - > direction ( ) = = QTimeLine : : Forward )
& & ( d - > timeLine - > state ( ) = = QTimeLine : : Running ) ;
2013-05-18 00:58:49 +00:00
}
QIcon KMessageWidget : : icon ( ) const
{
2015-06-04 01:08:57 +00:00
return d - > icon ;
2013-05-18 00:58:49 +00:00
}
2014-02-28 04:09:57 +00:00
void KMessageWidget : : setIcon ( const QIcon & icon )
2013-05-18 00:58:49 +00:00
{
2015-06-04 01:08:57 +00:00
d - > icon = icon ;
if ( d - > icon . isNull ( ) ) {
d - > iconLabel - > hide ( ) ;
} else {
const int size = style ( ) - > pixelMetric ( QStyle : : PM_ToolBarIconSize ) ;
d - > iconLabel - > setPixmap ( d - > icon . pixmap ( size ) ) ;
d - > iconLabel - > show ( ) ;
}
2013-05-18 00:58:49 +00:00
}
2015-06-04 01:08:57 +00:00
# include "moc_kmessagewidget.cpp"