Add files from breeze10

This commit is contained in:
Codic Kode 2020-04-25 14:20:33 -07:00
parent 4b084789fd
commit 8fb2644d49
45 changed files with 6935 additions and 0 deletions

124
CMakeLists.txt Normal file
View file

@ -0,0 +1,124 @@
project(breeze10)
set(PROJECT_VERSION "0.1")
set(PROJECT_VERSION_MAJOR 0)
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
include(WriteBasicConfigVersionFile)
include(FeatureSummary)
find_package(ECM 0.0.9 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake)
include(ECMInstallIcons)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(GenerateExportHeader)
# include(GtkUpdateIconCache)
find_package(KDecoration2 REQUIRED)
# old stuff
add_definitions(-DTRANSLATION_DOMAIN="breeze_kwin_deco")
find_package(KF5 REQUIRED COMPONENTS CoreAddons GuiAddons ConfigWidgets WindowSystem I18n)
find_package(Qt5 CONFIG REQUIRED COMPONENTS DBus)
### XCB
find_package(XCB COMPONENTS XCB)
set_package_properties(XCB PROPERTIES
DESCRIPTION "X protocol C-language Binding"
URL "http://xcb.freedesktop.org"
TYPE OPTIONAL
PURPOSE "Required to pass style properties to native Windows on X11 Platform"
)
if(UNIX AND NOT APPLE)
set(BREEZE_HAVE_X11 ${XCB_XCB_FOUND})
if (XCB_XCB_FOUND)
find_package(Qt5 REQUIRED CONFIG COMPONENTS X11Extras)
endif()
else()
set(BREEZE_HAVE_X11 FALSE)
endif()
################# configuration #################
configure_file(config-breeze.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-breeze.h )
################# includes #################
add_subdirectory(libbreezecommon)
################# newt target #################
### plugin classes
set(breeze10_SRCS
breezebutton.cpp
breezedecoration.cpp
breezeexceptionlist.cpp
breezesettingsprovider.cpp
breezesizegrip.cpp)
kconfig_add_kcfg_files(breeze10_SRCS breezesettings.kcfgc)
### config classes
### they are kept separately because they might move in a separate library in the future
set(breeze10_config_SRCS
config/breezeconfigwidget.cpp
config/breezedetectwidget.cpp
config/breezeexceptiondialog.cpp
config/breezeexceptionlistwidget.cpp
config/breezeexceptionmodel.cpp
config/breezeitemmodel.cpp
)
set(breeze10_config_PART_FORMS
config/ui/breezeconfigurationui.ui
config/ui/breezedetectwidget.ui
config/ui/breezeexceptiondialog.ui
config/ui/breezeexceptionlistwidget.ui
)
ki18n_wrap_ui(breeze10_config_PART_FORMS_HEADERS ${breeze10_config_PART_FORMS})
### build library
add_library(breeze10 MODULE
${breeze10_SRCS}
${breeze10_config_SRCS}
${breeze10_config_PART_FORMS_HEADERS})
target_link_libraries(breeze10
PUBLIC
Qt5::Core
Qt5::Gui
Qt5::DBus
PRIVATE
breeze10common5
KDecoration2::KDecoration
KF5::ConfigCore
KF5::CoreAddons
KF5::ConfigWidgets
KF5::GuiAddons
KF5::I18n
KF5::WindowSystem)
if(BREEZE_HAVE_X11)
target_link_libraries(breeze10
PUBLIC
Qt5::X11Extras
XCB::XCB)
endif()
install(TARGETS breeze10 DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kdecoration2)
install(FILES config/breeze10config.desktop DESTINATION ${SERVICES_INSTALL_DIR})
# install(TARGETS breezedecoration DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kdecoration2)
# install(FILES config/breezedecorationconfig.desktop DESTINATION ${SERVICES_INSTALL_DIR})

3
ChangeLog Normal file
View file

@ -0,0 +1,3 @@
V5.16
---------
* Initial release.

4
Messages.sh Normal file
View file

@ -0,0 +1,4 @@
#!bin/sh
$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp
$XGETTEXT `find . -name \*.cc -o -name \*.cpp -o -name \*.h` -o $podir/breeze_kwin_deco.pot
rm -f rc.cpp

5
NEWS Normal file
View file

@ -0,0 +1,5 @@
Latest version:
18 Jun 2019, V5.16
See "ChangeLog" for changes.

53
breeze.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef breeze_h
#define breeze_h
/*
* Copyright 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "breezesettings.h"
#include <QSharedPointer>
#include <QList>
namespace Breeze
{
//* convenience typedefs
using InternalSettingsPtr = QSharedPointer<InternalSettings>;
using InternalSettingsList = QList<InternalSettingsPtr>;
using InternalSettingsListIterator = QListIterator<InternalSettingsPtr>;
//* metrics
enum Metrics
{
// shadow dimensions (pixels)
Shadow_Overlap = 3,
};
//* exception
enum ExceptionMask
{
None = 0,
BorderSize = 1<<4
};
}
#endif

16
breeze.json Normal file
View file

@ -0,0 +1,16 @@
{
"KPlugin": {
"Description": "Window decoration using the Breeze visual style for the Plasma Desktop",
"EnabledByDefault": true,
"Id": "com.github.fauzie811.breeze10",
"Name": "Breeze10",
"ServiceTypes": [
"org.kde.kdecoration2"
]
},
"org.kde.kdecoration2": {
"blur": true,
"defaultTheme": "Breeze10",
"kcmodule": true
}
}

531
breezebutton.cpp Normal file
View file

@ -0,0 +1,531 @@
/*
* Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
* Copyright 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "breezebutton.h"
#include <KDecoration2/DecoratedClient>
#include <KColorUtils>
#include <QPainter>
namespace Breeze
{
using KDecoration2::ColorRole;
using KDecoration2::ColorGroup;
using KDecoration2::DecorationButtonType;
//__________________________________________________________________
Button::Button(DecorationButtonType type, Decoration* decoration, QObject* parent)
: DecorationButton(type, decoration, parent)
, m_animation( new QPropertyAnimation( this ) )
{
// setup animation
m_animation->setStartValue( 0 );
m_animation->setEndValue( 1.0 );
m_animation->setTargetObject( this );
m_animation->setPropertyName( "opacity" );
m_animation->setEasingCurve( QEasingCurve::InOutQuad );
// setup default geometry
const int height = decoration->buttonHeight();
const int width = height * (type == DecorationButtonType::Menu ? 1.0 : 1.5);
setGeometry(QRect(0, 0, width, height));
setIconSize(QSize( width, height ));
// connections
connect(decoration->client().data(), SIGNAL(iconChanged(QIcon)), this, SLOT(update()));
connect(decoration->settings().data(), &KDecoration2::DecorationSettings::reconfigured, this, &Button::reconfigure);
connect( this, &KDecoration2::DecorationButton::hoveredChanged, this, &Button::updateAnimationState );
reconfigure();
}
//__________________________________________________________________
Button::Button(QObject *parent, const QVariantList &args)
: Button(args.at(0).value<DecorationButtonType>(), args.at(1).value<Decoration*>(), parent)
{
m_flag = FlagStandalone;
//! icon size must return to !valid because it was altered from the default constructor,
//! in Standalone mode the button is not using the decoration metrics but its geometry
m_iconSize = QSize(-1, -1);
}
//__________________________________________________________________
Button *Button::create(DecorationButtonType type, KDecoration2::Decoration *decoration, QObject *parent)
{
if (auto d = qobject_cast<Decoration*>(decoration))
{
Button *b = new Button(type, d, parent);
switch( type )
{
case DecorationButtonType::Close:
b->setVisible( d->client().data()->isCloseable() );
QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::closeableChanged, b, &Breeze::Button::setVisible );
break;
case DecorationButtonType::Maximize:
b->setVisible( d->client().data()->isMaximizeable() );
QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::maximizeableChanged, b, &Breeze::Button::setVisible );
break;
case DecorationButtonType::Minimize:
b->setVisible( d->client().data()->isMinimizeable() );
QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::minimizeableChanged, b, &Breeze::Button::setVisible );
break;
case DecorationButtonType::ContextHelp:
b->setVisible( d->client().data()->providesContextHelp() );
QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::providesContextHelpChanged, b, &Breeze::Button::setVisible );
break;
case DecorationButtonType::Shade:
b->setVisible( d->client().data()->isShadeable() );
QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::shadeableChanged, b, &Breeze::Button::setVisible );
break;
case DecorationButtonType::Menu:
QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::iconChanged, b, [b]() { b->update(); });
break;
default: break;
}
return b;
}
return nullptr;
}
//__________________________________________________________________
void Button::paint(QPainter *painter, const QRect &repaintRegion)
{
Q_UNUSED(repaintRegion)
if (!decoration()) return;
painter->save();
// translate from offset
if( m_flag == FlagFirstInList ) painter->translate( m_offset );
else painter->translate( 0, m_offset.y() );
if( !m_iconSize.isValid() ) m_iconSize = geometry().size().toSize();
// menu button
if (type() == DecorationButtonType::Menu)
{
// const QRectF iconRect( geometry().topLeft(), m_iconSize );
const int menuIconSize = (qobject_cast<Decoration *>(decoration()))->iconSize();
const qreal topLeft = (geometry().width()/2) - (menuIconSize/2);
const QRectF iconRect(topLeft + geometry().left(), topLeft + geometry().top(), menuIconSize, menuIconSize);
decoration()->client().data()->icon().paint(painter, iconRect.toRect());
} else {
drawIcon( painter );
}
painter->restore();
}
//__________________________________________________________________
void Button::drawIcon( QPainter *painter ) const
{
painter->setRenderHints( QPainter::Antialiasing );
/*
scale painter so that its window matches QRect( 0, 0, 45, 30 )
this makes all further rendering and scaling simpler
all further rendering is preformed inside QRect( 0, 0, 45, 30 )
*/
painter->translate( geometry().topLeft() );
const qreal height( m_iconSize.height() );
const qreal width( m_iconSize.width() );
if ( height != 30 )
painter->scale( width/45, height/30 );
// render background
const QColor backgroundColor( this->backgroundColor() );
auto d = qobject_cast<Decoration*>( decoration() );
bool isInactive(d && !d->client().data()->isActive()
&& !isHovered() && !isPressed()
&& m_animation->state() != QPropertyAnimation::Running);
QColor inactiveCol(Qt::gray);
if (isInactive)
{
int gray = qGray(d->titleBarColor().rgb());
if (gray <= 200) {
gray += 55;
gray = qMax(gray, 115);
}
else gray -= 45;
inactiveCol = QColor(gray, gray, gray);
}
// render mark
const QColor foregroundColor( this->foregroundColor() );
if( foregroundColor.isValid() )
{
// setup painter
QPen pen( foregroundColor );
pen.setCapStyle( Qt::FlatCap );
pen.setJoinStyle( Qt::MiterJoin );
// pen.setWidthF( 1.0*qMax((qreal)1.0, 30/width ) );
pen.setWidthF( 1.0 );
switch( type() )
{
case DecorationButtonType::Close:
{
if( backgroundColor.isValid() )
{
painter->setPen( Qt::NoPen );
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 0, 0, 45, 30 ) );
}
pen.setWidthF( 1.1 );
painter->setPen( pen );
painter->setBrush( Qt::NoBrush );
painter->drawLine( QPointF( 18, 10 ), QPointF( 28, 20 ) );
painter->drawLine( QPointF( 18, 20 ), QPointF( 28, 10 ) );
pen.setWidthF( 1.0 );
break;
}
case DecorationButtonType::Maximize:
{
if( backgroundColor.isValid() )
{
painter->setPen( Qt::NoPen );
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 0, 0, 45, 30 ) );
}
painter->setPen( pen );
painter->setBrush( Qt::NoBrush );
if (isChecked())
{
painter->drawRect(QRectF(18.5, 12.5, 7.0, 7.0));
painter->drawPolyline(QPolygonF()
<< QPointF(20.5, 12.5) << QPointF(20.5, 10.5) << QPointF(27.5, 10.5) << QPointF(27.5, 17.5) << QPointF(25.5, 17.5));
}
else {
painter->drawRect(QRectF(18.5, 10.5, 9.0, 9.0));
}
break;
}
case DecorationButtonType::Minimize:
{
if( backgroundColor.isValid() )
{
painter->setPen( Qt::NoPen );
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 0, 0, 45, 30 ) );
}
painter->setPen( pen );
painter->setBrush( Qt::NoBrush );
painter->drawLine( QPointF( 18, 15.5 ), QPointF( 28, 15.5 ) );
break;
}
case DecorationButtonType::OnAllDesktops:
{
painter->setPen( Qt::NoPen );
if( backgroundColor.isValid() )
{
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 0, 0, 45, 30 ) );
}
painter->setBrush( foregroundColor );
if( isChecked()) {
// outer ring
painter->drawRect( QRectF( 16, 9, 12, 12 ) );
// center dot
QColor backgroundColor( this->backgroundColor() );
if( !backgroundColor.isValid() && d ) backgroundColor = d->titleBarColor();
if( backgroundColor.isValid() )
{
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 21, 14, 2, 2 ) );
}
} else {
painter->drawPolygon( QPolygonF()
<< QPointF( 19.5, 14.5 )
<< QPointF( 25, 9 )
<< QPointF( 28, 12 )
<< QPointF( 22.5, 17.5 ) );
painter->setPen( pen );
painter->drawLine( QPointF( 18.5, 13.5 ), QPointF( 23.5, 18.5 ) );
painter->drawLine( QPointF( 25, 12 ), QPointF( 17.5, 19.5 ) );
}
break;
}
case DecorationButtonType::Shade:
{
if( backgroundColor.isValid() )
{
painter->setPen( Qt::NoPen );
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 0, 0, 45, 30 ) );
}
painter->setPen( pen );
painter->setBrush( Qt::NoBrush );
painter->drawLine( 18, 12, 26, 12 );
if (isChecked()) {
painter->drawPolyline( QPolygonF()
<< QPointF( 18, 15 )
<< QPointF( 22, 19 )
<< QPointF( 26, 15 ) );
} else {
painter->drawPolyline( QPolygonF()
<< QPointF( 18, 19 )
<< QPointF( 22, 15 )
<< QPointF( 26, 19 ) );
}
break;
}
case DecorationButtonType::KeepBelow:
{
if( backgroundColor.isValid() )
{
painter->setPen( Qt::NoPen );
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 0, 0, 45, 30 ) );
}
painter->setPen( pen );
painter->setBrush( Qt::NoBrush );
painter->drawPolyline( QPolygonF()
<< QPointF( 18, 11 )
<< QPointF( 22, 15 )
<< QPointF( 26, 11 ) );
painter->drawPolyline( QPolygonF()
<< QPointF( 18, 15 )
<< QPointF( 22, 19 )
<< QPointF( 26, 15 ) );
break;
}
case DecorationButtonType::KeepAbove:
{
if( backgroundColor.isValid() )
{
painter->setPen( Qt::NoPen );
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 0, 0, 45, 30 ) );
}
painter->setPen( pen );
painter->setBrush( Qt::NoBrush );
painter->drawPolyline( QPolygonF()
<< QPointF( 18, 15 )
<< QPointF( 22, 11 )
<< QPointF( 26, 15 ) );
painter->drawPolyline( QPolygonF()
<< QPointF( 18, 19 )
<< QPointF( 22, 15 )
<< QPointF( 26, 19 ) );
break;
}
case DecorationButtonType::ApplicationMenu:
{
if( backgroundColor.isValid() )
{
painter->setPen( Qt::NoPen );
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 0, 0, 45, 30 ) );
}
painter->setPen( pen );
painter->setBrush( Qt::NoBrush );
painter->drawLine( QPointF( 15, 11.5 ), QPointF( 30, 11.5 ) );
painter->drawLine( QPointF( 15, 15.5 ), QPointF( 30, 15.5 ) );
painter->drawLine( QPointF( 15, 19.5 ), QPointF( 30, 19.5 ) );
break;
}
case DecorationButtonType::ContextHelp:
{
if( backgroundColor.isValid() )
{
painter->setPen( Qt::NoPen );
painter->setBrush( backgroundColor );
painter->drawRect( QRectF( 0, 0, 45, 30 ) );
}
painter->setPen( pen );
painter->setBrush( Qt::NoBrush );
QPainterPath path;
path.moveTo( 18, 12 );
path.arcTo( QRectF( 18, 9.5, 8, 5 ), 180, -180 );
path.cubicTo( QPointF(26.5, 15.5), QPointF( 22, 13.5 ), QPointF( 22, 17.5 ) );
painter->drawPath( path );
painter->drawPoint( 22, 21 );
break;
}
default: break;
}
}
}
//__________________________________________________________________
QColor Button::foregroundColor() const
{
auto d = qobject_cast<Decoration*>( decoration() );
if( !d ) {
return QColor();
} else if( ( type() == DecorationButtonType::KeepBelow || type() == DecorationButtonType::KeepAbove ) && isChecked() ) {
return d->titleBarColor();
} else if( m_animation->state() == QPropertyAnimation::Running && type() == DecorationButtonType::Close ) {
return KColorUtils::mix( d->fontColor(), Qt::white, m_opacity );
} else if( isHovered() && type() == DecorationButtonType::Close ) {
return Qt::white;
} else {
return d->fontColor();
}
}
//__________________________________________________________________
QColor Button::backgroundColor() const
{
auto d = qobject_cast<Decoration*>( decoration() );
if( !d ) {
return QColor();
}
if( isPressed() ) {
if( type() == DecorationButtonType::Close ) return KColorUtils::mix( Qt::red, Qt::black, 0.3 );
else return KColorUtils::mix( d->titleBarColor(), d->fontColor(), 0.3 );
} else if( ( type() == DecorationButtonType::KeepBelow || type() == DecorationButtonType::KeepAbove ) && isChecked() ) {
return d->fontColor();
} else if( m_animation->state() == QPropertyAnimation::Running ) {
if( type() == DecorationButtonType::Close ) return KColorUtils::mix( d->titleBarColor(), Qt::red, m_opacity );
else {
QColor color( KColorUtils::mix( d->titleBarColor(), d->fontColor(), 0.2 ) );
color.setAlpha( color.alpha()*m_opacity );
return color;
}
} else if( isHovered() ) {
if( type() == DecorationButtonType::Close ) return Qt::red;
else return KColorUtils::mix( d->titleBarColor(), d->fontColor(), 0.2 );
} else {
return QColor();
}
}
//________________________________________________________________
void Button::reconfigure()
{
// animation
auto d = qobject_cast<Decoration*>(decoration());
if( d ) m_animation->setDuration( d->internalSettings()->animationsDuration() );
}
//__________________________________________________________________
void Button::updateAnimationState( bool hovered )
{
auto d = qobject_cast<Decoration*>(decoration());
if( !(d && d->internalSettings()->animationsEnabled() ) ) return;
QAbstractAnimation::Direction dir = hovered ? QPropertyAnimation::Forward : QPropertyAnimation::Backward;
if( m_animation->state() == QPropertyAnimation::Running && m_animation->direction() != dir )
m_animation->stop();
m_animation->setDirection( dir );
if( m_animation->state() != QPropertyAnimation::Running ) m_animation->start();
}
} // namespace

140
breezebutton.h Normal file
View file

@ -0,0 +1,140 @@
#ifndef BREEZE_BUTTONS_H
#define BREEZE_BUTTONS_H
/*
* Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
* Copyright 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <KDecoration2/DecorationButton>
#include "breezedecoration.h"
#include <QHash>
#include <QImage>
#include <QPropertyAnimation>
namespace Breeze
{
class Button : public KDecoration2::DecorationButton
{
Q_OBJECT
//* declare active state opacity
Q_PROPERTY( qreal opacity READ opacity WRITE setOpacity )
public:
//* constructor
explicit Button(QObject *parent, const QVariantList &args);
//* destructor
virtual ~Button() = default;
//* button creation
static Button *create(KDecoration2::DecorationButtonType type, KDecoration2::Decoration *decoration, QObject *parent);
//* render
virtual void paint(QPainter *painter, const QRect &repaintRegion) override;
//* flag
enum Flag
{
FlagNone,
FlagStandalone,
FlagFirstInList,
FlagLastInList
};
//* flag
void setFlag( Flag value )
{ m_flag = value; }
//* standalone buttons
bool isStandAlone() const { return m_flag == FlagStandalone; }
//* offset
void setOffset( const QPointF& value )
{ m_offset = value; }
//* horizontal offset, for rendering
void setHorizontalOffset( qreal value )
{ m_offset.setX( value ); }
//* vertical offset, for rendering
void setVerticalOffset( qreal value )
{ m_offset.setY( value ); }
//* set icon size
void setIconSize( const QSize& value )
{ m_iconSize = value; }
//*@name active state change animation
//@{
void setOpacity( qreal value )
{
if( m_opacity == value ) return;
m_opacity = value;
update();
}
qreal opacity() const
{ return m_opacity; }
//@}
private Q_SLOTS:
//* apply configuration changes
void reconfigure();
//* animation state
void updateAnimationState(bool);
private:
//* private constructor
explicit Button(KDecoration2::DecorationButtonType type, Decoration *decoration, QObject *parent = nullptr);
//* draw button icon
void drawIcon( QPainter *) const;
//*@name colors
//@{
QColor foregroundColor() const;
QColor backgroundColor() const;
//@}
Flag m_flag = FlagNone;
//* active state change animation
QPropertyAnimation *m_animation;
//* vertical offset (for rendering)
QPointF m_offset;
//* icon size
QSize m_iconSize;
//* active state change opacity
qreal m_opacity = 0;
};
} // namespace
#endif

768
breezedecoration.cpp Normal file
View file

@ -0,0 +1,768 @@
/*
* Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
* Copyright 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "breezedecoration.h"
#include "breeze.h"
#include "breezesettingsprovider.h"
#include "config-breeze.h"
#include "config/breezeconfigwidget.h"
#include "breezebutton.h"
#include "breezesizegrip.h"
#include "breezeboxshadowrenderer.h"
#include <KDecoration2/DecoratedClient>
#include <KDecoration2/DecorationButtonGroup>
#include <KDecoration2/DecorationSettings>
#include <KDecoration2/DecorationShadow>
#include <KConfigGroup>
#include <KColorUtils>
#include <KSharedConfig>
#include <KPluginFactory>
#include <QPainter>
#include <QTextStream>
#include <QTimer>
#if BREEZE_HAVE_X11
#include <QX11Info>
#endif
#include <cmath>
K_PLUGIN_FACTORY_WITH_JSON(
BreezeDecoFactory,
"breeze.json",
registerPlugin<Breeze::Decoration>();
registerPlugin<Breeze::Button>(QStringLiteral("button"));
registerPlugin<Breeze::ConfigWidget>(QStringLiteral("kcmodule"));
)
namespace
{
struct ShadowParams {
ShadowParams()
: offset(QPoint(0, 0))
, radius(0)
, opacity(0) {}
ShadowParams(const QPoint &offset, int radius, qreal opacity)
: offset(offset)
, radius(radius)
, opacity(opacity) {}
QPoint offset;
int radius;
qreal opacity;
};
struct CompositeShadowParams {
CompositeShadowParams() = default;
CompositeShadowParams(
const QPoint &offset,
const ShadowParams &shadow1,
const ShadowParams &shadow2)
: offset(offset)
, shadow1(shadow1)
, shadow2(shadow2) {}
bool isNone() const {
return qMax(shadow1.radius, shadow2.radius) == 0;
}
QPoint offset;
ShadowParams shadow1;
ShadowParams shadow2;
};
const CompositeShadowParams s_shadowParams[] = {
// None
CompositeShadowParams(),
// Small
CompositeShadowParams(
QPoint(0, 4),
ShadowParams(QPoint(0, 0), 16, 1),
ShadowParams(QPoint(0, -2), 8, 0.4)),
// Medium
CompositeShadowParams(
QPoint(0, 8),
ShadowParams(QPoint(0, 0), 32, 0.9),
ShadowParams(QPoint(0, -4), 16, 0.3)),
// Large
CompositeShadowParams(
QPoint(0, 12),
ShadowParams(QPoint(0, 0), 48, 0.8),
ShadowParams(QPoint(0, -6), 24, 0.2)),
// Very large
CompositeShadowParams(
QPoint(0, 16),
ShadowParams(QPoint(0, 0), 64, 0.7),
ShadowParams(QPoint(0, -8), 32, 0.1)),
};
inline CompositeShadowParams lookupShadowParams(int size)
{
switch (size) {
case Breeze::InternalSettings::ShadowNone:
return s_shadowParams[0];
case Breeze::InternalSettings::ShadowSmall:
return s_shadowParams[1];
case Breeze::InternalSettings::ShadowMedium:
return s_shadowParams[2];
case Breeze::InternalSettings::ShadowLarge:
return s_shadowParams[3];
case Breeze::InternalSettings::ShadowVeryLarge:
return s_shadowParams[4];
default:
// Fallback to the Large size.
return s_shadowParams[3];
}
}
}
namespace Breeze
{
using KDecoration2::ColorRole;
using KDecoration2::ColorGroup;
using KDecoration2::DecorationButtonType;
//________________________________________________________________
static int g_sDecoCount = 0;
static int g_shadowSizeEnum = InternalSettings::ShadowLarge;
static int g_shadowStrength = 255;
static QColor g_shadowColor = Qt::black;
static QSharedPointer<KDecoration2::DecorationShadow> g_sShadow;
//________________________________________________________________
Decoration::Decoration(QObject *parent, const QVariantList &args)
: KDecoration2::Decoration(parent, args)
, m_animation( new QPropertyAnimation( this ) )
{
g_sDecoCount++;
}
//________________________________________________________________
Decoration::~Decoration()
{
g_sDecoCount--;
if (g_sDecoCount == 0) {
// last deco destroyed, clean up shadow
g_sShadow.clear();
}
deleteSizeGrip();
}
//________________________________________________________________
void Decoration::setOpacity( qreal value )
{
if( m_opacity == value ) return;
m_opacity = value;
update();
if( m_sizeGrip ) m_sizeGrip->update();
}
//________________________________________________________________
QColor Decoration::titleBarColor() const
{
auto c = client().data();
if( hideTitleBar() ) return c->color( ColorGroup::Inactive, ColorRole::TitleBar );
else if( m_animation->state() == QPropertyAnimation::Running )
{
return KColorUtils::mix(
c->color( ColorGroup::Inactive, ColorRole::TitleBar ),
c->color( ColorGroup::Active, ColorRole::TitleBar ),
m_opacity );
} else return c->color( c->isActive() ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::TitleBar );
}
//________________________________________________________________
QColor Decoration::fontColor() const
{
auto c = client().data();
if( m_animation->state() == QPropertyAnimation::Running )
{
return KColorUtils::mix(
c->color( ColorGroup::Inactive, ColorRole::Foreground ),
c->color( ColorGroup::Active, ColorRole::Foreground ),
m_opacity );
} else return c->color( c->isActive() ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::Foreground );
}
//________________________________________________________________
void Decoration::init()
{
auto c = client().data();
// active state change animation
m_animation->setStartValue( 0 );
m_animation->setEndValue( 1.0 );
m_animation->setTargetObject( this );
m_animation->setPropertyName( "opacity" );
m_animation->setEasingCurve( QEasingCurve::InOutQuad );
reconfigure();
updateTitleBar();
auto s = settings();
connect(s.data(), &KDecoration2::DecorationSettings::borderSizeChanged, this, &Decoration::recalculateBorders);
// a change in font might cause the borders to change
recalculateBorders();
connect(s.data(), &KDecoration2::DecorationSettings::spacingChanged, this, &Decoration::recalculateBorders);
// buttons
connect(s.data(), &KDecoration2::DecorationSettings::spacingChanged, this, &Decoration::updateButtonsGeometryDelayed);
connect(s.data(), &KDecoration2::DecorationSettings::decorationButtonsLeftChanged, this, &Decoration::updateButtonsGeometryDelayed);
connect(s.data(), &KDecoration2::DecorationSettings::decorationButtonsRightChanged, this, &Decoration::updateButtonsGeometryDelayed);
// full reconfiguration
connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, this, &Decoration::reconfigure);
connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, SettingsProvider::self(), &SettingsProvider::reconfigure, Qt::UniqueConnection );
connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, this, &Decoration::updateButtonsGeometryDelayed);
connect(c, &KDecoration2::DecoratedClient::adjacentScreenEdgesChanged, this, &Decoration::recalculateBorders);
connect(c, &KDecoration2::DecoratedClient::maximizedHorizontallyChanged, this, &Decoration::recalculateBorders);
connect(c, &KDecoration2::DecoratedClient::maximizedVerticallyChanged, this, &Decoration::recalculateBorders);
connect(c, &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::recalculateBorders);
connect(c, &KDecoration2::DecoratedClient::captionChanged, this,
[this]()
{
// update the caption area
update(titleBar());
}
);
connect(c, &KDecoration2::DecoratedClient::activeChanged, this, &Decoration::updateAnimationState);
connect(c, &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateTitleBar);
connect(c, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateTitleBar);
//connect(c, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::setOpaque);
connect(c, &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateButtonsGeometry);
connect(c, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateButtonsGeometry);
connect(c, &KDecoration2::DecoratedClient::adjacentScreenEdgesChanged, this, &Decoration::updateButtonsGeometry);
connect(c, &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::updateButtonsGeometry);
createButtons();
createShadow();
}
//________________________________________________________________
void Decoration::updateTitleBar()
{
auto s = settings();
auto c = client().data();
const bool maximized = isMaximized();
const int width = c->width() - (borderSize()*2);
const int height = maximized ? borderTop() : borderTop() - borderSize();
const int x = borderSize();
const int y = maximized ? 0 : borderSize();
setTitleBar(QRect(x, y, width, height));
}
//________________________________________________________________
void Decoration::updateAnimationState()
{
if( m_internalSettings->animationsEnabled() )
{
auto c = client().data();
m_animation->setDirection( c->isActive() ? QPropertyAnimation::Forward : QPropertyAnimation::Backward );
if( m_animation->state() != QPropertyAnimation::Running ) m_animation->start();
} else {
update();
}
}
//________________________________________________________________
void Decoration::updateSizeGripVisibility()
{
auto c = client().data();
if( m_sizeGrip )
{ m_sizeGrip->setVisible( c->isResizeable() && !isMaximized() && !c->isShaded() ); }
}
//________________________________________________________________
int Decoration::borderSize(bool bottom) const
{
const int baseSize = settings()->smallSpacing();
if( m_internalSettings && (m_internalSettings->mask() & BorderSize ) )
{
switch (m_internalSettings->borderSize()) {
case InternalSettings::BorderNone: return 0;
case InternalSettings::BorderNoSides: return bottom ? qMax(4, baseSize) : 0;
default:
case InternalSettings::BorderTiny: return baseSize/2;
case InternalSettings::BorderNormal: return baseSize;
case InternalSettings::BorderLarge: return baseSize*2;
case InternalSettings::BorderVeryLarge: return baseSize*3;
case InternalSettings::BorderHuge: return baseSize*4;
case InternalSettings::BorderVeryHuge: return baseSize*5;
case InternalSettings::BorderOversized: return baseSize*8;
}
} else {
switch (settings()->borderSize()) {
case KDecoration2::BorderSize::None: return 0;
case KDecoration2::BorderSize::NoSides: return bottom ? qMax(4, baseSize) : 0;
default:
case KDecoration2::BorderSize::Tiny: return baseSize/2;
case KDecoration2::BorderSize::Normal: return baseSize;
case KDecoration2::BorderSize::Large: return baseSize*2;
case KDecoration2::BorderSize::VeryLarge: return baseSize*3;
case KDecoration2::BorderSize::Huge: return baseSize*4;
case KDecoration2::BorderSize::VeryHuge: return baseSize*5;
case KDecoration2::BorderSize::Oversized: return baseSize*8;
}
}
}
//________________________________________________________________
void Decoration::reconfigure()
{
m_internalSettings = SettingsProvider::self()->internalSettings( this );
// animation
m_animation->setDuration( m_internalSettings->animationsDuration() );
// borders
recalculateBorders();
// shadow
createShadow();
// size grip
if( hasNoBorders() && m_internalSettings->drawSizeGrip() ) createSizeGrip();
else deleteSizeGrip();
}
//________________________________________________________________
void Decoration::recalculateBorders()
{
auto c = client().data();
auto s = settings();
// left, right and bottom borders
const int left = isLeftEdge() ? 0 : borderSize();
const int right = isRightEdge() ? 0 : borderSize();
const int bottom = (c->isShaded() || isBottomEdge()) ? 0 : borderSize(true);
int top = 0;
if( hideTitleBar() ) top = bottom;
else {
top += isMaximized() ? 0 : borderSize();
QFont f; f.fromString(m_internalSettings->titleBarFont());
QFontMetrics fm(f);
top += qMax(fm.height(), buttonHeight() );
}
setBorders(QMargins(left, top, right, bottom));
// extended sizes
const int extSize = s->largeSpacing();
int extSides = 0;
int extTop = 0;
int extBottom = 0;
if( hasNoBorders() || borderSize() < 2 )
{
extSides = extSize;
extBottom = extSize;
extTop = extSize;
} else if( hasNoSideBorders() ) {
extSides = extSize;
}
setResizeOnlyBorders(QMargins(extSides, extTop, extSides, extBottom));
}
//________________________________________________________________
void Decoration::createButtons()
{
m_leftButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Left, this, &Button::create);
m_rightButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Right, this, &Button::create);
updateButtonsGeometry();
}
//________________________________________________________________
void Decoration::updateButtonsGeometryDelayed()
{ QTimer::singleShot( 0, this, &Decoration::updateButtonsGeometry ); }
//________________________________________________________________
void Decoration::updateButtonsGeometry()
{
const auto s = settings();
// adjust button position
const int bHeight = buttonHeight();
foreach( const QPointer<KDecoration2::DecorationButton>& button, m_leftButtons->buttons() + m_rightButtons->buttons() )
{
const int bWidth = buttonHeight() * (button.data()->type() == DecorationButtonType::Menu ? 1.0 : 1.5);
button.data()->setGeometry( QRectF( QPoint( 0, 0 ), QSizeF( bWidth, bHeight ) ) );
static_cast<Button*>( button.data() )->setIconSize( QSize( bWidth, bHeight ) );
}
const bool maximized = isMaximized();
// left buttons
if( !m_leftButtons->buttons().isEmpty() )
{
m_leftButtons->setSpacing(0);
m_leftButtons->setPos(QPointF(borderLeft(), maximized ? 0 : borderSize()));
}
// right buttons
if( !m_rightButtons->buttons().isEmpty() )
{
m_rightButtons->setSpacing(0);
// padding
m_rightButtons->setPos(QPointF(size().width() - m_rightButtons->geometry().width() - borderRight(), maximized ? 0 : borderSize()));
}
update();
}
//________________________________________________________________
void Decoration::paint(QPainter *painter, const QRect &repaintRegion)
{
// TODO: optimize based on repaintRegion
auto c = client().data();
auto s = settings();
const bool maximized = isMaximized();
// paint background
if( borderSize() > 0 )
{
painter->fillRect(rect(), Qt::transparent);
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::NoPen);
QColor winCol = c->isActive() ? KColorUtils::mix( titleBarColor(), Qt::black, 0.3 ) : titleBarColor();
winCol.setAlpha(titleBarAlpha());
painter->setBrush(winCol);
// clip away the titlebar part
if( !hideTitleBar() )
{
QRegion outerReg(rect());
QRegion innerReg(QRect(borderLeft(), maximized ? 0 : borderSize(), size().width() - borderLeft() - borderRight(), buttonHeight()));
painter->setClipRegion(outerReg.subtracted(innerReg));
}
painter->drawRect( rect() );
painter->restore();
}
if( !hideTitleBar() ) paintTitleBar(painter, repaintRegion);
if( hasBorders() && !s->isAlphaChannelSupported() )
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
painter->setBrush( Qt::NoBrush );
painter->setPen( c->isActive() ?
c->color( ColorGroup::Active, ColorRole::TitleBar ):
c->color( ColorGroup::Inactive, ColorRole::Foreground ) );
painter->drawRect( rect().adjusted( 0, 0, -1, -1 ) );
painter->restore();
}
}
//________________________________________________________________
void Decoration::paintTitleBar(QPainter *painter, const QRect &repaintRegion)
{
const auto c = client().data();
const bool maximized = isMaximized();
const QRect titleRect(QPoint(borderLeft(), maximized ? 0 : borderSize()), QSize(size().width() - borderLeft() - borderRight(), buttonHeight()));
if ( !titleRect.intersects(repaintRegion) ) return;
painter->save();
painter->setPen(Qt::NoPen);
QColor titleBarColor = this->titleBarColor();
titleBarColor.setAlpha(titleBarAlpha());
painter->setBrush( titleBarColor );
auto s = settings();
painter->drawRect(titleRect);
painter->restore();
// draw caption
QFont f; f.fromString(m_internalSettings->titleBarFont());
// KDE needs this FIXME: Why?
QFontDatabase fd; f.setStyleName(fd.styleString(f));
painter->setFont(f);
painter->setPen( fontColor() );
const auto cR = captionRect();
const QString caption = painter->fontMetrics().elidedText(c->caption(), Qt::ElideMiddle, cR.first.width());
painter->drawText(cR.first, cR.second | Qt::TextSingleLine, caption);
// draw all buttons
m_leftButtons->paint(painter, repaintRegion);
m_rightButtons->paint(painter, repaintRegion);
}
//________________________________________________________________
int Decoration::buttonHeight() const
{
const int baseSize = 10; //settings()->gridUnit();
switch( m_internalSettings->buttonSize() )
{
case InternalSettings::ButtonTiny: return baseSize;
case InternalSettings::ButtonSmall: return baseSize*2;
default:
case InternalSettings::ButtonDefault: return baseSize*3;
case InternalSettings::ButtonLarge: return baseSize*4;
case InternalSettings::ButtonVeryLarge: return baseSize*5;
}
}
//________________________________________________________________
int Decoration::iconSize() const
{
const int baseSize = 16;
switch( m_internalSettings->buttonSize() )
{
case InternalSettings::ButtonTiny: return baseSize/2;
case InternalSettings::ButtonSmall: return baseSize;
default:
case InternalSettings::ButtonDefault: return baseSize;
case InternalSettings::ButtonLarge: return baseSize*1.5;
case InternalSettings::ButtonVeryLarge: return baseSize*2;
}
}
//________________________________________________________________
QPair<QRect,Qt::Alignment> Decoration::captionRect() const
{
if( hideTitleBar() ) return qMakePair( QRect(), Qt::AlignCenter );
else {
auto c = client().data();
int leftOffset = m_leftButtons->buttons().isEmpty() ?
4.0*settings()->smallSpacing():
m_leftButtons->geometry().x() + m_leftButtons->geometry().width() + 4.0*settings()->smallSpacing();
if (!m_leftButtons->buttons().isEmpty()
&& m_leftButtons->buttons().last().data()->type() == DecorationButtonType::Menu
&& m_internalSettings->titleAlignment() == InternalSettings::AlignLeft)
{
leftOffset -= 4.0 * settings()->smallSpacing();
}
const int rightOffset = m_rightButtons->buttons().isEmpty() ?
4.0*settings()->smallSpacing() :
size().width() - m_rightButtons->geometry().x() + 4.0*settings()->smallSpacing();
const int yOffset = isMaximized() ? 0 : borderSize();
const QRect maxRect( leftOffset, yOffset, size().width() - leftOffset - rightOffset, buttonHeight() );
switch( m_internalSettings->titleAlignment() )
{
case InternalSettings::AlignLeft:
return qMakePair( maxRect, Qt::AlignVCenter|Qt::AlignLeft );
case InternalSettings::AlignRight:
return qMakePair( maxRect, Qt::AlignVCenter|Qt::AlignRight );
case InternalSettings::AlignCenter:
return qMakePair( maxRect, Qt::AlignCenter );
default:
case InternalSettings::AlignCenterFullWidth:
{
// full caption rect
const QRect fullRect = QRect( 0, yOffset, size().width(), buttonHeight() );
QFont f; f.fromString(m_internalSettings->titleBarFont());
QFontMetrics fm(f);
QRect boundingRect( fm.boundingRect( c->caption()) );
// text bounding rect
boundingRect.setTop( yOffset );
boundingRect.setHeight( buttonHeight() );
boundingRect.moveLeft( ( size().width() - boundingRect.width() )/2 );
if( boundingRect.left() < leftOffset ) return qMakePair( maxRect, Qt::AlignVCenter|Qt::AlignLeft );
else if( boundingRect.right() > size().width() - rightOffset ) return qMakePair( maxRect, Qt::AlignVCenter|Qt::AlignRight );
else return qMakePair(fullRect, Qt::AlignCenter);
}
}
}
}
//________________________________________________________________
void Decoration::createShadow()
{
if (!g_sShadow
||g_shadowSizeEnum != m_internalSettings->shadowSize()
|| g_shadowStrength != m_internalSettings->shadowStrength()
|| g_shadowColor != m_internalSettings->shadowColor())
{
g_shadowSizeEnum = m_internalSettings->shadowSize();
g_shadowStrength = m_internalSettings->shadowStrength();
g_shadowColor = m_internalSettings->shadowColor();
const CompositeShadowParams params = lookupShadowParams(g_shadowSizeEnum);
if (params.isNone()) {
g_sShadow.clear();
setShadow(g_sShadow);
return;
}
auto withOpacity = [](const QColor &color, qreal opacity) -> QColor {
QColor c(color);
c.setAlphaF(opacity);
return c;
};
const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius)
.expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius));
BoxShadowRenderer shadowRenderer;
shadowRenderer.setBorderRadius(0.5);
shadowRenderer.setBoxSize(boxSize);
shadowRenderer.setDevicePixelRatio(1.0); // TODO: Create HiDPI shadows?
const qreal strength = static_cast<qreal>(g_shadowStrength) / 255.0;
shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius,
withOpacity(g_shadowColor, params.shadow1.opacity * strength));
shadowRenderer.addShadow(params.shadow2.offset, params.shadow2.radius,
withOpacity(g_shadowColor, params.shadow2.opacity * strength));
QImage shadowTexture = shadowRenderer.render();
QPainter painter(&shadowTexture);
painter.setRenderHint(QPainter::Antialiasing);
const QRect outerRect = shadowTexture.rect();
QRect boxRect(QPoint(0, 0), boxSize);
boxRect.moveCenter(outerRect.center());
// Mask out inner rect.
const QMargins padding = QMargins(
boxRect.left() - outerRect.left() - Metrics::Shadow_Overlap - params.offset.x(),
boxRect.top() - outerRect.top() - Metrics::Shadow_Overlap - params.offset.y(),
outerRect.right() - boxRect.right() - Metrics::Shadow_Overlap + params.offset.x(),
outerRect.bottom() - boxRect.bottom() - Metrics::Shadow_Overlap + params.offset.y());
const QRect innerRect = outerRect - padding;
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::black);
painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
painter.drawRoundedRect(innerRect, 0.5, 0.5);
// Draw outline.
painter.setPen(withOpacity(g_shadowColor, 0.2 * strength));
painter.setBrush(Qt::NoBrush);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawRoundedRect(innerRect, -0.5, -0.5);
painter.end();
g_sShadow = QSharedPointer<KDecoration2::DecorationShadow>::create();
g_sShadow->setPadding(padding);
g_sShadow->setInnerShadowRect(QRect(outerRect.center(), QSize(1, 1)));
g_sShadow->setShadow(shadowTexture);
}
setShadow(g_sShadow);
}
//_________________________________________________________________
void Decoration::createSizeGrip()
{
// do nothing if size grip already exist
if( m_sizeGrip ) return;
#if BREEZE_HAVE_X11
if( !QX11Info::isPlatformX11() ) return;
// access client
auto c = client().data();
if( !c ) return;
if( c->windowId() != 0 )
{
m_sizeGrip = new SizeGrip( this );
connect( c, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateSizeGripVisibility );
connect( c, &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::updateSizeGripVisibility );
connect( c, &KDecoration2::DecoratedClient::resizeableChanged, this, &Decoration::updateSizeGripVisibility );
}
#endif
}
//_________________________________________________________________
void Decoration::deleteSizeGrip()
{
if( m_sizeGrip )
{
m_sizeGrip->deleteLater();
m_sizeGrip = nullptr;
}
}
} // namespace
#include "breezedecoration.moc"

221
breezedecoration.h Normal file
View file

@ -0,0 +1,221 @@
#ifndef BREEZE_DECORATION_H
#define BREEZE_DECORATION_H
/*
* Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
* Copyright 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "breeze.h"
#include "breezesettings.h"
#include <KDecoration2/Decoration>
#include <KDecoration2/DecoratedClient>
#include <KDecoration2/DecorationSettings>
#include <QPalette>
#include <QPropertyAnimation>
#include <QVariant>
namespace KDecoration2
{
class DecorationButton;
class DecorationButtonGroup;
}
namespace Breeze
{
class SizeGrip;
class Decoration : public KDecoration2::Decoration
{
Q_OBJECT
//* declare active state opacity
Q_PROPERTY( qreal opacity READ opacity WRITE setOpacity )
public:
//* constructor
explicit Decoration(QObject *parent = nullptr, const QVariantList &args = QVariantList());
//* destructor
virtual ~Decoration();
//* paint
void paint(QPainter *painter, const QRect &repaintRegion) override;
//* internal settings
InternalSettingsPtr internalSettings() const
{ return m_internalSettings; }
//* caption height
int captionHeight() const;
//* button height
int buttonHeight() const;
//* icon size
int iconSize() const;
//*@name active state change animation
//@{
void setOpacity( qreal );
qreal opacity() const
{ return m_opacity; }
//@}
//*@name colors
//@{
QColor titleBarColor() const;
QColor outlineColor() const;
QColor fontColor() const;
//@}
//*@name maximization modes
//@{
inline bool isMaximized() const;
inline bool isMaximizedHorizontally() const;
inline bool isMaximizedVertically() const;
inline bool isLeftEdge() const;
inline bool isRightEdge() const;
inline bool isTopEdge() const;
inline bool isBottomEdge() const;
inline bool hideTitleBar() const;
//@}
public Q_SLOTS:
void init() override;
private Q_SLOTS:
void reconfigure();
void recalculateBorders();
void updateButtonsGeometry();
void updateButtonsGeometryDelayed();
void updateTitleBar();
void updateAnimationState();
void updateSizeGripVisibility();
private:
//* return the rect in which caption will be drawn
QPair<QRect,Qt::Alignment> captionRect() const;
void createButtons();
void paintTitleBar(QPainter *painter, const QRect &repaintRegion);
void createShadow();
//*@name border size
//@{
int borderSize(bool bottom = false) const;
inline bool hasBorders() const;
inline bool hasNoBorders() const;
inline bool hasNoSideBorders() const;
//@}
//*@name color customization
//@{
inline bool opaqueTitleBar() const;
inline int titleBarAlpha() const;
//@}
//*@name size grip
//@{
void createSizeGrip();
void deleteSizeGrip();
SizeGrip* sizeGrip() const
{ return m_sizeGrip; }
//@}
InternalSettingsPtr m_internalSettings;
KDecoration2::DecorationButtonGroup *m_leftButtons = nullptr;
KDecoration2::DecorationButtonGroup *m_rightButtons = nullptr;
//* size grip widget
SizeGrip *m_sizeGrip = nullptr;
//* active state change animation
QPropertyAnimation *m_animation;
//* active state change opacity
qreal m_opacity = 0;
};
bool Decoration::hasBorders() const
{
if( m_internalSettings && m_internalSettings->mask() & BorderSize ) return m_internalSettings->borderSize() > InternalSettings::BorderNoSides;
else return settings()->borderSize() > KDecoration2::BorderSize::NoSides;
}
bool Decoration::hasNoBorders() const
{
if( m_internalSettings && m_internalSettings->mask() & BorderSize ) return m_internalSettings->borderSize() == InternalSettings::BorderNone;
else return settings()->borderSize() == KDecoration2::BorderSize::None;
}
bool Decoration::hasNoSideBorders() const
{
if( m_internalSettings && m_internalSettings->mask() & BorderSize ) return m_internalSettings->borderSize() == InternalSettings::BorderNoSides;
else return settings()->borderSize() == KDecoration2::BorderSize::NoSides;
}
bool Decoration::isMaximized() const
{ return client().data()->isMaximized() && !m_internalSettings->drawBorderOnMaximizedWindows(); }
bool Decoration::isMaximizedHorizontally() const
{ return client().data()->isMaximizedHorizontally() && !m_internalSettings->drawBorderOnMaximizedWindows(); }
bool Decoration::isMaximizedVertically() const
{ return client().data()->isMaximizedVertically() && !m_internalSettings->drawBorderOnMaximizedWindows(); }
bool Decoration::isLeftEdge() const
{ return (client().data()->isMaximizedHorizontally() || client().data()->adjacentScreenEdges().testFlag( Qt::LeftEdge ) ) && !m_internalSettings->drawBorderOnMaximizedWindows(); }
bool Decoration::isRightEdge() const
{ return (client().data()->isMaximizedHorizontally() || client().data()->adjacentScreenEdges().testFlag( Qt::RightEdge ) ) && !m_internalSettings->drawBorderOnMaximizedWindows(); }
bool Decoration::isTopEdge() const
{ return (client().data()->isMaximizedVertically() || client().data()->adjacentScreenEdges().testFlag( Qt::TopEdge ) ) && !m_internalSettings->drawBorderOnMaximizedWindows(); }
bool Decoration::isBottomEdge() const
{ return (client().data()->isMaximizedVertically() || client().data()->adjacentScreenEdges().testFlag( Qt::BottomEdge ) ) && !m_internalSettings->drawBorderOnMaximizedWindows(); }
bool Decoration::hideTitleBar() const
{ return m_internalSettings->hideTitleBar() && !client().data()->isShaded(); }
bool Decoration::opaqueTitleBar() const
{ return m_internalSettings->opaqueTitleBar(); }
int Decoration::titleBarAlpha() const
{
if (m_internalSettings->opaqueTitleBar())
return 255;
int a = m_internalSettings->opacityOverride() > -1 ? m_internalSettings->opacityOverride()
: m_internalSettings->backgroundOpacity();
a = qBound(0, a, 100);
return qRound(static_cast<qreal>(a) * static_cast<qreal>(2.55));
}
}
#endif

131
breezeexceptionlist.cpp Normal file
View file

@ -0,0 +1,131 @@
//////////////////////////////////////////////////////////////////////////////
// breezeexceptionlist.cpp
// window decoration exceptions
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezeexceptionlist.h"
namespace Breeze
{
//______________________________________________________________
void ExceptionList::readConfig( KSharedConfig::Ptr config )
{
_exceptions.clear();
QString groupName;
for( int index = 0; config->hasGroup( groupName = exceptionGroupName( index ) ); ++index )
{
// create exception
InternalSettings exception;
// reset group
readConfig( &exception, config.data(), groupName );
// create new configuration
InternalSettingsPtr configuration( new InternalSettings() );
configuration.data()->load();
// apply changes from exception
configuration->setEnabled( exception.enabled() );
configuration->setExceptionType( exception.exceptionType() );
configuration->setExceptionPattern( exception.exceptionPattern() );
configuration->setMask( exception.mask() );
// propagate all features found in mask to the output configuration
if( exception.mask() & BorderSize ) configuration->setBorderSize( exception.borderSize() );
configuration->setHideTitleBar( exception.hideTitleBar() );
configuration->setOpaqueTitleBar( exception.opaqueTitleBar() );
configuration->setOpacityOverride( exception.opacityOverride() );
configuration->setIsDialog( exception.isDialog() );
// append to exceptions
_exceptions.append( configuration );
}
}
//______________________________________________________________
void ExceptionList::writeConfig( KSharedConfig::Ptr config )
{
// remove all existing exceptions
QString groupName;
for( int index = 0; config->hasGroup( groupName = exceptionGroupName( index ) ); ++index )
{ config->deleteGroup( groupName ); }
// rewrite current exceptions
int index = 0;
foreach( const InternalSettingsPtr& exception, _exceptions )
{
writeConfig( exception.data(), config.data(), exceptionGroupName( index ) );
++index;
}
}
//_______________________________________________________________________
QString ExceptionList::exceptionGroupName( int index )
{ return QString( "Windeco Exception %1" ).arg( index ); }
//______________________________________________________________
void ExceptionList::writeConfig( KCoreConfigSkeleton* skeleton, KConfig* config, const QString& groupName )
{
// list of items to be written
QStringList keys = { "Enabled", "ExceptionPattern", "ExceptionType", "HideTitleBar", "IsDialog", "OpaqueTitleBar", "OpacityOverride", "Mask", "BorderSize"};
// write all items
foreach( auto key, keys )
{
KConfigSkeletonItem* item( skeleton->findItem( key ) );
if( !item ) continue;
if( !groupName.isEmpty() ) item->setGroup( groupName );
KConfigGroup configGroup( config, item->group() );
configGroup.writeEntry( item->key(), item->property() );
}
}
//______________________________________________________________
void ExceptionList::readConfig( KCoreConfigSkeleton* skeleton, KConfig* config, const QString& groupName )
{
foreach( KConfigSkeletonItem* item, skeleton->items() )
{
if( !groupName.isEmpty() ) item->setGroup( groupName );
item->readConfig( config );
}
}
}

79
breezeexceptionlist.h Normal file
View file

@ -0,0 +1,79 @@
#ifndef breezeexceptionlist_h
#define breezeexceptionlist_h
//////////////////////////////////////////////////////////////////////////////
// breezeexceptionlist.h
// window decoration exceptions
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezesettings.h"
#include "breeze.h"
#include <KSharedConfig>
namespace Breeze
{
//! breeze exceptions list
class ExceptionList
{
public:
//! constructor from list
explicit ExceptionList( const InternalSettingsList& exceptions = InternalSettingsList() ):
_exceptions( exceptions )
{}
//! exceptions
const InternalSettingsList& get( void ) const
{ return _exceptions; }
//! read from KConfig
void readConfig( KSharedConfig::Ptr );
//! write to kconfig
void writeConfig( KSharedConfig::Ptr );
protected:
//! generate exception group name for given exception index
static QString exceptionGroupName( int index );
//! read configuration
static void readConfig( KCoreConfigSkeleton*, KConfig*, const QString& );
//! write configuration
static void writeConfig( KCoreConfigSkeleton*, KConfig*, const QString& );
private:
//! exceptions
InternalSettingsList _exceptions;
};
}
#endif

6
breezesettings.kcfgc Normal file
View file

@ -0,0 +1,6 @@
File=breezesettingsdata.kcfg
ClassName=InternalSettings
NameSpace=Breeze
Singleton=false
Mutators=true
GlobalEnums=true

152
breezesettingsdata.kcfg Normal file
View file

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd">
<kcfg>
<kcfgfile name="breezerc"/>
<!-- common options -->
<group name="Common">
<entry name="ShadowStrength" type = "Int">
<default>255</default>
<min>25</min>
<max>255</max>
</entry>
<!-- shadow -->
<entry name="ShadowSize" type = "Enum">
<choices>
<choice name="ShadowNone"/>
<choice name="ShadowSmall"/>
<choice name="ShadowMedium"/>
<choice name="ShadowLarge"/>
<choice name="ShadowVeryLarge"/>
</choices>
<default>ShadowLarge</default>
</entry>
<entry name="ShadowColor" type = "Color">
<default>0, 0, 0</default>
</entry>
<!-- close button -->
<entry name="OutlineCloseButton" type = "Bool">
<default>true</default>
</entry>
</group>
<group name="Windeco">
<!-- border size -->
<!--
this one is used only for window specific settings, since the default is globally set by kwin
the enumeration must be kept in sync with kwin's global settings
-->
<entry name="BorderSize" type = "Enum">
<choices>
<choice name="BorderNone" />
<choice name="BorderNoSides" />
<choice name="BorderTiny" />
<choice name="BorderNormal" />
<choice name="BorderLarge" />
<choice name="BorderVeryLarge" />
<choice name="BorderHuge" />
<choice name="BorderVeryHuge" />
<choice name="BorderOversized" />
</choices>
<default>BorderNoSides</default>
</entry>
<!-- title alignment -->
<entry name="TitleAlignment" type="Enum">
<choices>
<choice name="AlignLeft" />
<choice name="AlignCenter" />
<choice name="AlignCenterFullWidth" />
<choice name="AlignRight" />
</choices>
<default>AlignCenterFullWidth</default>
</entry>
<!-- button size -->
<entry name="ButtonSize" type="Enum">
<choices>
<choice name="ButtonTiny" />
<choice name="ButtonSmall" />
<choice name="ButtonDefault" />
<choice name="ButtonLarge" />
<choice name="ButtonVeryLarge" />
</choices>
<default>ButtonDefault</default>
</entry>
<!-- maximized windows -->
<entry name="DrawBorderOnMaximizedWindows" type = "Bool">
<default>false</default>
</entry>
<entry name="DrawTitleBarSeparator" type = "Bool">
<default>false</default>
</entry>
<entry name="BackgroundOpacity" type = "Int">
<default>100</default>
</entry>
<entry name="TitleBarFont" type = "String"/>
<!-- size grip -->
<entry name="DrawSizeGrip" type = "Bool">
<default>false</default>
</entry>
<!-- animations -->
<entry name="AnimationsEnabled" type = "Bool">
<default>true</default>
</entry>
<entry name="AnimationsDuration" type = "Int">
<default>150</default>
</entry>
<!-- hide title bar -->
<entry name="HideTitleBar" type = "Bool">
<default>false</default>
</entry>
<!-- opaque title bar -->
<entry name="OpaqueTitleBar" type = "Bool">
<default>false</default>
</entry>
<entry name="OpacityOverride" type = "Int">
<default>-1</default>
</entry>
<!-- dialogs -->
<entry name="IsDialog" type = "Bool">
<default>false</default>
</entry>
<!-- window specific settings -->
<entry name="ExceptionType" type="Enum">
<choices>
<choice name="ExceptionWindowClassName" />
<choice name="ExceptionWindowTitle" />
</choices>
<default>ExceptionWindowClassName</default>
</entry>
<entry name="ExceptionPattern" type = "String"/>
<entry name="Enabled" type = "Bool">
<default>true</default>
</entry>
<entry name="Mask" type = "Int">
<default>0</default>
</entry>
</group>
</kcfg>

139
breezesettingsprovider.cpp Normal file
View file

@ -0,0 +1,139 @@
/*
* Copyright 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "breezesettingsprovider.h"
#include "breezeexceptionlist.h"
#include <KWindowInfo>
#include <QTextStream>
namespace Breeze
{
SettingsProvider *SettingsProvider::s_self = nullptr;
//__________________________________________________________________
SettingsProvider::SettingsProvider():
m_config( KSharedConfig::openConfig( QStringLiteral("breezerc") ) )
{ reconfigure(); }
//__________________________________________________________________
SettingsProvider::~SettingsProvider()
{ s_self = nullptr; }
//__________________________________________________________________
SettingsProvider *SettingsProvider::self()
{
// TODO: this is not thread safe!
if (!s_self)
{ s_self = new SettingsProvider(); }
return s_self;
}
//__________________________________________________________________
void SettingsProvider::reconfigure()
{
if( !m_defaultSettings )
{
m_defaultSettings = InternalSettingsPtr(new InternalSettings());
m_defaultSettings->setCurrentGroup( QStringLiteral("Windeco") );
}
m_defaultSettings->load();
ExceptionList exceptions;
exceptions.readConfig( m_config );
m_exceptions = exceptions.get();
}
//__________________________________________________________________
InternalSettingsPtr SettingsProvider::internalSettings( Decoration *decoration ) const
{
QString windowTitle;
QString className;
// get the client
auto client = decoration->client().data();
foreach( auto internalSettings, m_exceptions )
{
// discard disabled exceptions
if( !internalSettings->enabled() ) continue;
// discard exceptions with empty exception pattern
if( internalSettings->exceptionPattern().isEmpty() ) continue;
if (internalSettings->isDialog())
{
KWindowInfo info(client->windowId(), NET::WMWindowType);
if (info.valid()
&& info.windowType(NET::NormalMask | NET::DialogMask) != NET::Dialog) {
continue;
}
}
/*
decide which value is to be compared
to the regular expression, based on exception type
*/
QString value;
switch( internalSettings->exceptionType() )
{
case InternalSettings::ExceptionWindowTitle:
{
value = windowTitle.isEmpty() ? (windowTitle = client->caption()):windowTitle;
break;
}
default:
case InternalSettings::ExceptionWindowClassName:
{
if( className.isEmpty() )
{
// retrieve class name
KWindowInfo info( client->windowId(), nullptr, NET::WM2WindowClass );
QString window_className( QString::fromUtf8(info.windowClassName()) );
QString window_class( QString::fromUtf8(info.windowClassClass()) );
className = window_className + QStringLiteral(" ") + window_class;
}
value = className;
break;
}
}
// check matching
if( QRegExp( internalSettings->exceptionPattern() ).indexIn( value ) >= 0 )
{ return internalSettings; }
}
return m_defaultSettings;
}
}

76
breezesettingsprovider.h Normal file
View file

@ -0,0 +1,76 @@
#ifndef breezesettingsprovider_h
#define breezesettingsprovider_h
/*
* Copyright 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "breezedecoration.h"
#include "breezesettings.h"
#include "breeze.h"
#include <KSharedConfig>
#include <QObject>
namespace Breeze
{
class SettingsProvider: public QObject
{
Q_OBJECT
public:
//* destructor
~SettingsProvider();
//* singleton
static SettingsProvider *self();
//* internal settings for given decoration
InternalSettingsPtr internalSettings(Decoration *) const;
public Q_SLOTS:
//* reconfigure
void reconfigure();
private:
//* contructor
SettingsProvider();
//* default configuration
InternalSettingsPtr m_defaultSettings;
//* exceptions
InternalSettingsList m_exceptions;
//* config object
KSharedConfigPtr m_config;
//* singleton
static SettingsProvider *s_self;
};
}
#endif

303
breezesizegrip.cpp Normal file
View file

@ -0,0 +1,303 @@
/*************************************************************************
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
*************************************************************************/
#include "breezesizegrip.h"
#include <KDecoration2/DecoratedClient>
#include <QPainter>
#include <QPolygon>
#include <QTimer>
#if BREEZE_HAVE_X11
#include <QX11Info>
#endif
namespace Breeze
{
//* scoped pointer convenience typedef
template <typename T> using ScopedPointer = QScopedPointer<T, QScopedPointerPodDeleter>;
//_____________________________________________
SizeGrip::SizeGrip( Decoration* decoration ):QWidget(nullptr)
,m_decoration( decoration )
{
setAttribute(Qt::WA_NoSystemBackground );
setAutoFillBackground( false );
// cursor
setCursor( Qt::SizeFDiagCursor );
// size
setFixedSize( QSize( GripSize, GripSize ) );
// mask
setMask( QRegion( QVector<QPoint>{
QPoint( 0, GripSize ),
QPoint( GripSize, 0 ),
QPoint( GripSize, GripSize ),
QPoint( 0, GripSize )} ) );
// embed
embed();
updatePosition();
// connections
auto c = decoration->client().data();
connect( c, &KDecoration2::DecoratedClient::widthChanged, this, &SizeGrip::updatePosition );
connect( c, &KDecoration2::DecoratedClient::heightChanged, this, &SizeGrip::updatePosition );
connect( c, &KDecoration2::DecoratedClient::activeChanged, this, &SizeGrip::updateActiveState );
// show
show();
}
//_____________________________________________
SizeGrip::~SizeGrip()
{}
//_____________________________________________
void SizeGrip::updateActiveState()
{
#if BREEZE_HAVE_X11
if( QX11Info::isPlatformX11() )
{
const quint32 value = XCB_STACK_MODE_ABOVE;
xcb_configure_window( QX11Info::connection(), winId(), XCB_CONFIG_WINDOW_STACK_MODE, &value );
xcb_map_window( QX11Info::connection(), winId() );
}
#endif
update();
}
//_____________________________________________
void SizeGrip::embed()
{
#if BREEZE_HAVE_X11
if( !QX11Info::isPlatformX11() ) return;
auto c = m_decoration.data()->client().data();
xcb_window_t windowId = c->windowId();
if( windowId )
{
/*
find client's parent
we want the size grip to be at the same level as the client in the stack
*/
xcb_window_t current = windowId;
auto connection = QX11Info::connection();
xcb_query_tree_cookie_t cookie = xcb_query_tree_unchecked( connection, current );
ScopedPointer<xcb_query_tree_reply_t> tree(xcb_query_tree_reply( connection, cookie, nullptr ) );
if( !tree.isNull() && tree->parent ) current = tree->parent;
// reparent
xcb_reparent_window( connection, winId(), current, 0, 0 );
setWindowTitle( "Breeze::SizeGrip" );
} else {
hide();
}
#endif
}
//_____________________________________________
void SizeGrip::paintEvent( QPaintEvent* )
{
if( !m_decoration ) return;
// get relevant colors
const QColor backgroundColor( m_decoration.data()->titleBarColor() );
// create and configure painter
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing );
painter.setPen( Qt::NoPen );
painter.setBrush( backgroundColor );
// polygon
painter.drawPolygon( QVector<QPoint> {
QPoint( 0, GripSize ),
QPoint( GripSize, 0 ),
QPoint( GripSize, GripSize ),
QPoint( 0, GripSize )} );
}
//_____________________________________________
void SizeGrip::mousePressEvent( QMouseEvent* event )
{
switch (event->button())
{
case Qt::RightButton:
{
hide();
QTimer::singleShot(5000, this, SLOT(show()));
break;
}
case Qt::MidButton:
{
hide();
break;
}
case Qt::LeftButton:
if( rect().contains( event->pos() ) )
{ sendMoveResizeEvent( event->pos() ); }
break;
default: break;
}
}
//_______________________________________________________________________________
void SizeGrip::updatePosition()
{
#if BREEZE_HAVE_X11
if( !QX11Info::isPlatformX11() ) return;
auto c = m_decoration.data()->client().data();
QPoint position(
c->width() - GripSize - Offset,
c->height() - GripSize - Offset );
quint32 values[2] = { quint32(position.x()), quint32(position.y()) };
xcb_configure_window( QX11Info::connection(), winId(), XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values );
#endif
}
//_____________________________________________
void SizeGrip::sendMoveResizeEvent( QPoint position )
{
#if BREEZE_HAVE_X11
if( !QX11Info::isPlatformX11() ) return;
// pointer to connection
auto connection( QX11Info::connection() );
// client
auto c = m_decoration.data()->client().data();
/*
get root position matching position
need to use xcb because the embedding of the widget
breaks QT's mapToGlobal and other methods
*/
QPoint rootPosition( position );
xcb_get_geometry_cookie_t cookie( xcb_get_geometry( connection, winId() ) );
ScopedPointer<xcb_get_geometry_reply_t> reply( xcb_get_geometry_reply( connection, cookie, nullptr ) );
if( reply )
{
// translate coordinates
xcb_translate_coordinates_cookie_t coordCookie( xcb_translate_coordinates(
connection, winId(), reply.data()->root,
-reply.data()->border_width,
-reply.data()->border_width ) );
ScopedPointer< xcb_translate_coordinates_reply_t> coordReply( xcb_translate_coordinates_reply( connection, coordCookie, nullptr ) );
if( coordReply )
{
rootPosition.rx() += coordReply.data()->dst_x;
rootPosition.ry() += coordReply.data()->dst_y;
}
}
// move/resize atom
if( !m_moveResizeAtom )
{
// create atom if not found
const QString atomName( "_NET_WM_MOVERESIZE" );
xcb_intern_atom_cookie_t cookie( xcb_intern_atom( connection, false, atomName.size(), qPrintable( atomName ) ) );
ScopedPointer<xcb_intern_atom_reply_t> reply( xcb_intern_atom_reply( connection, cookie, nullptr ) );
m_moveResizeAtom = reply ? reply->atom:0;
}
if( !m_moveResizeAtom ) return;
// button release event
xcb_button_release_event_t releaseEvent;
memset(&releaseEvent, 0, sizeof(releaseEvent));
releaseEvent.response_type = XCB_BUTTON_RELEASE;
releaseEvent.event = winId();
releaseEvent.child = XCB_WINDOW_NONE;
releaseEvent.root = QX11Info::appRootWindow();
releaseEvent.event_x = position.x();
releaseEvent.event_y = position.y();
releaseEvent.root_x = rootPosition.x();
releaseEvent.root_y = rootPosition.y();
releaseEvent.detail = XCB_BUTTON_INDEX_1;
releaseEvent.state = XCB_BUTTON_MASK_1;
releaseEvent.time = XCB_CURRENT_TIME;
releaseEvent.same_screen = true;
xcb_send_event( connection, false, winId(), XCB_EVENT_MASK_BUTTON_RELEASE, reinterpret_cast<const char*>(&releaseEvent));
xcb_ungrab_pointer( connection, XCB_TIME_CURRENT_TIME );
// move resize event
xcb_client_message_event_t clientMessageEvent;
memset(&clientMessageEvent, 0, sizeof(clientMessageEvent));
clientMessageEvent.response_type = XCB_CLIENT_MESSAGE;
clientMessageEvent.type = m_moveResizeAtom;
clientMessageEvent.format = 32;
clientMessageEvent.window = c->windowId();
clientMessageEvent.data.data32[0] = rootPosition.x();
clientMessageEvent.data.data32[1] = rootPosition.y();
clientMessageEvent.data.data32[2] = 4; // bottom right
clientMessageEvent.data.data32[3] = Qt::LeftButton;
clientMessageEvent.data.data32[4] = 0;
xcb_send_event( connection, false, QX11Info::appRootWindow(),
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
reinterpret_cast<const char*>(&clientMessageEvent) );
xcb_flush( connection );
#endif
}
}

100
breezesizegrip.h Normal file
View file

@ -0,0 +1,100 @@
#ifndef breezesizegrip_h
#define breezesizegrip_h
/*************************************************************************
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
*************************************************************************/
#include "breezedecoration.h"
#include "config-breeze.h"
#include <QMouseEvent>
#include <QPaintEvent>
#include <QWidget>
#include <QPointer>
#if BREEZE_HAVE_X11
#include <xcb/xcb.h>
#endif
namespace Breeze
{
//* implements size grip for all widgets
class SizeGrip: public QWidget
{
Q_OBJECT
public:
//* constructor
explicit SizeGrip( Decoration* );
//* constructor
virtual ~SizeGrip();
protected Q_SLOTS:
//* update background color
void updateActiveState();
//* update position
void updatePosition();
//* embed into parent widget
void embed();
protected:
//*@name event handlers
//@{
//* paint
virtual void paintEvent( QPaintEvent* ) override;
//* mouse press
virtual void mousePressEvent( QMouseEvent* ) override;
//@}
private:
//* send resize event
void sendMoveResizeEvent( QPoint );
//* grip size
enum {
Offset = 0,
GripSize = 14
};
//* decoration
QPointer<Decoration> m_decoration;
//* move/resize atom
#if BREEZE_HAVE_X11
xcb_atom_t m_moveResizeAtom = 0;
#endif
};
}
#endif

View file

@ -0,0 +1,20 @@
# Find the FFTW library
#
# Usage:
# find_package(FFTW [REQUIRED])
#
# It sets the following variables:
# FFTW_FOUND
# FFTW_INCLUDES
# FFTW_LIBRARIES
find_path(FFTW_INCLUDES fftw3.h)
find_library(FFTW_LIBRARIES NAMES fftw3)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(FFTW DEFAULT_MSG
FFTW_INCLUDES FFTW_LIBRARIES)
mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES)

28
config-breeze.h.cmake Normal file
View file

@ -0,0 +1,28 @@
/* config-breeze.h. Generated by cmake from config-breeze.h.cmake */
/*************************************************************************
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
*************************************************************************/
#ifndef config_breeze_h
#define config_breeze_h
/* Define to 1 if XCB libraries are found */
#cmakedefine01 BREEZE_HAVE_X11
#endif

View file

@ -0,0 +1,15 @@
[Desktop Entry]
Exec=kcmshell5 breeze10config
Icon=preferences-system-windows
Type=Service
X-KDE-ServiceTypes=KCModule
X-KDE-Library=org.kde.kdecoration2/breeze10
X-KDE-PluginKeyword=kcmodule
X-KDE-ParentApp=kcontrol
X-KDE-Weight=60
X-KDE-PluginInfo-Name=Breeze10
Name=Breeze10
Comment=Modify the appearance of window decorations
X-KDE-Keywords=breeze10,decoration

View file

@ -0,0 +1,323 @@
//////////////////////////////////////////////////////////////////////////////
// breezeconfigurationui.cpp
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezeconfigwidget.h"
#include "breezeexceptionlist.h"
#include "breezesettings.h"
#include <KLocalizedString>
#include <QDBusConnection>
#include <QDBusMessage>
namespace Breeze
{
//_________________________________________________________
ConfigWidget::ConfigWidget( QWidget* parent, const QVariantList &args ):
KCModule(parent, args),
m_configuration( KSharedConfig::openConfig( QStringLiteral( "breezerc" ) ) ),
m_changed( false )
{
// configuration
m_ui.setupUi( this );
// track ui changes
connect( m_ui.titleAlignment, SIGNAL(currentIndexChanged(int)), SLOT(updateChanged()) );
connect( m_ui.buttonSize, SIGNAL(currentIndexChanged(int)), SLOT(updateChanged()) );
connect( m_ui.drawBorderOnMaximizedWindows, SIGNAL(clicked()), SLOT(updateChanged()) );
connect( m_ui.drawSizeGrip, SIGNAL(clicked()), SLOT(updateChanged()) );
connect( m_ui.opacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged), [=](int /*i*/){updateChanged();} );
connect( m_ui.fontComboBox, &QFontComboBox::currentFontChanged, [this] { updateChanged(); } );
connect( m_ui.fontSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), [=](int /*i*/){updateChanged();} );
connect(m_ui.weightComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [this] { updateChanged(); } );
connect( m_ui.italicCheckBox, &QCheckBox::stateChanged, [this] { updateChanged(); } );
// track animations changes
connect( m_ui.animationsEnabled, SIGNAL(clicked()), SLOT(updateChanged()) );
connect( m_ui.animationsDuration, SIGNAL(valueChanged(int)), SLOT(updateChanged()) );
// track shadows changes
connect( m_ui.shadowSize, SIGNAL(currentIndexChanged(int)), SLOT(updateChanged()) );
connect( m_ui.shadowStrength, SIGNAL(valueChanged(int)), SLOT(updateChanged()) );
connect( m_ui.shadowColor, SIGNAL(changed(QColor)), SLOT(updateChanged()) );
// track exception changes
connect( m_ui.exceptions, SIGNAL(changed(bool)), SLOT(updateChanged()) );
}
//_________________________________________________________
void ConfigWidget::load()
{
// create internal settings and load from rc files
m_internalSettings = InternalSettingsPtr( new InternalSettings() );
m_internalSettings->load();
// assign to ui
m_ui.titleAlignment->setCurrentIndex( m_internalSettings->titleAlignment() );
m_ui.buttonSize->setCurrentIndex( m_internalSettings->buttonSize() );
m_ui.drawBorderOnMaximizedWindows->setChecked( m_internalSettings->drawBorderOnMaximizedWindows() );
m_ui.drawSizeGrip->setChecked( m_internalSettings->drawSizeGrip() );
m_ui.animationsEnabled->setChecked( m_internalSettings->animationsEnabled() );
m_ui.animationsDuration->setValue( m_internalSettings->animationsDuration() );
m_ui.opacitySpinBox->setValue( m_internalSettings->backgroundOpacity() );
QString fontStr = m_internalSettings->titleBarFont();
if (fontStr.isEmpty())
fontStr = QLatin1String("Sans,11,-1,5,50,0,0,0,0,0");
QFont f; f.fromString( fontStr );
m_ui.fontComboBox->setCurrentFont( f );
m_ui.fontSizeSpinBox->setValue( f.pointSize() );
int w = f.weight();
switch (w) {
case QFont::Medium:
m_ui.weightComboBox->setCurrentIndex(1);
break;
case QFont::DemiBold:
m_ui.weightComboBox->setCurrentIndex(2);
break;
case QFont::Bold:
m_ui.weightComboBox->setCurrentIndex(3);
break;
case QFont::ExtraBold:
m_ui.weightComboBox->setCurrentIndex(4);
break;
case QFont::Black:
m_ui.weightComboBox->setCurrentIndex(5);
break;
default:
m_ui.weightComboBox->setCurrentIndex(0);
break;
}
m_ui.italicCheckBox->setChecked( f.italic() );
// load shadows
if( m_internalSettings->shadowSize() <= InternalSettings::ShadowVeryLarge ) m_ui.shadowSize->setCurrentIndex( m_internalSettings->shadowSize() );
else m_ui.shadowSize->setCurrentIndex( InternalSettings::ShadowLarge );
m_ui.shadowStrength->setValue( qRound(qreal(m_internalSettings->shadowStrength()*100)/255 ) );
m_ui.shadowColor->setColor( m_internalSettings->shadowColor() );
// load exceptions
ExceptionList exceptions;
exceptions.readConfig( m_configuration );
m_ui.exceptions->setExceptions( exceptions.get() );
setChanged( false );
}
//_________________________________________________________
void ConfigWidget::save()
{
// create internal settings and load from rc files
m_internalSettings = InternalSettingsPtr( new InternalSettings() );
m_internalSettings->load();
// apply modifications from ui
m_internalSettings->setTitleAlignment( m_ui.titleAlignment->currentIndex() );
m_internalSettings->setButtonSize( m_ui.buttonSize->currentIndex() );
m_internalSettings->setDrawBorderOnMaximizedWindows( m_ui.drawBorderOnMaximizedWindows->isChecked() );
m_internalSettings->setDrawSizeGrip( m_ui.drawSizeGrip->isChecked() );
m_internalSettings->setAnimationsEnabled( m_ui.animationsEnabled->isChecked() );
m_internalSettings->setAnimationsDuration( m_ui.animationsDuration->value() );
m_internalSettings->setBackgroundOpacity(m_ui.opacitySpinBox->value());
QFont f = m_ui.fontComboBox->currentFont();
f.setPointSize(m_ui.fontSizeSpinBox->value());
int indx = m_ui.weightComboBox->currentIndex();
switch (indx) {
case 1:
f.setWeight(QFont::Medium);
break;
case 2:
f.setWeight(QFont::DemiBold);
break;
case 3:
f.setWeight(QFont::Bold);
break;
case 4:
f.setWeight(QFont::ExtraBold);
break;
case 5:
f.setWeight(QFont::Black);
break;
default:
f.setBold(false);
break;
}
f.setItalic(m_ui.italicCheckBox->isChecked());
m_internalSettings->setTitleBarFont(f.toString());
m_internalSettings->setShadowSize( m_ui.shadowSize->currentIndex() );
m_internalSettings->setShadowStrength( qRound( qreal(m_ui.shadowStrength->value()*255)/100 ) );
m_internalSettings->setShadowColor( m_ui.shadowColor->color() );
// save configuration
m_internalSettings->save();
// get list of exceptions and write
InternalSettingsList exceptions( m_ui.exceptions->exceptions() );
ExceptionList( exceptions ).writeConfig( m_configuration );
// sync configuration
m_configuration->sync();
setChanged( false );
// needed to tell kwin to reload when running from external kcmshell
{
QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
QDBusConnection::sessionBus().send(message);
}
// needed for breeze style to reload shadows
{
QDBusMessage message( QDBusMessage::createSignal("/BreezeDecoration", "org.kde.Breeze.Style", "reparseConfiguration") );
QDBusConnection::sessionBus().send(message);
}
}
//_________________________________________________________
void ConfigWidget::defaults()
{
// create internal settings and load from rc files
m_internalSettings = InternalSettingsPtr( new InternalSettings() );
m_internalSettings->setDefaults();
// assign to ui
m_ui.titleAlignment->setCurrentIndex( m_internalSettings->titleAlignment() );
m_ui.buttonSize->setCurrentIndex( m_internalSettings->buttonSize() );
m_ui.drawBorderOnMaximizedWindows->setChecked( m_internalSettings->drawBorderOnMaximizedWindows() );
m_ui.drawSizeGrip->setChecked( m_internalSettings->drawSizeGrip() );
m_ui.animationsEnabled->setChecked( m_internalSettings->animationsEnabled() );
m_ui.animationsDuration->setValue( m_internalSettings->animationsDuration() );
m_ui.opacitySpinBox->setValue( m_internalSettings->backgroundOpacity() );
QFont f; f.fromString("Sans,11,-1,5,50,0,0,0,0,0");
m_ui.fontComboBox->setCurrentFont( f );
m_ui.fontSizeSpinBox->setValue( f.pointSize() );
int w = f.weight();
switch (w) {
case QFont::Medium:
m_ui.weightComboBox->setCurrentIndex(1);
break;
case QFont::DemiBold:
m_ui.weightComboBox->setCurrentIndex(2);
break;
case QFont::Bold:
m_ui.weightComboBox->setCurrentIndex(3);
break;
case QFont::ExtraBold:
m_ui.weightComboBox->setCurrentIndex(4);
break;
case QFont::Black:
m_ui.weightComboBox->setCurrentIndex(5);
break;
default:
m_ui.weightComboBox->setCurrentIndex(0);
break;
}
m_ui.italicCheckBox->setChecked( f.italic() );
m_ui.shadowSize->setCurrentIndex( m_internalSettings->shadowSize() );
m_ui.shadowStrength->setValue( qRound(qreal(m_internalSettings->shadowStrength()*100)/255 ) );
m_ui.shadowColor->setColor( m_internalSettings->shadowColor() );
}
//_______________________________________________
void ConfigWidget::updateChanged()
{
// check configuration
if( !m_internalSettings ) return;
// track modifications
bool modified( false );
QFont f; f.fromString( m_internalSettings->titleBarFont() );
if( m_ui.titleAlignment->currentIndex() != m_internalSettings->titleAlignment() ) modified = true;
else if( m_ui.buttonSize->currentIndex() != m_internalSettings->buttonSize() ) modified = true;
else if( m_ui.drawBorderOnMaximizedWindows->isChecked() != m_internalSettings->drawBorderOnMaximizedWindows() ) modified = true;
else if( m_ui.drawSizeGrip->isChecked() != m_internalSettings->drawSizeGrip() ) modified = true;
else if( m_ui.opacitySpinBox->value() != m_internalSettings->backgroundOpacity() ) modified = true;
// font (also see below)
else if( m_ui.fontComboBox->currentFont().toString() != f.family() ) modified = true;
else if( m_ui.fontSizeSpinBox->value() != f.pointSize() ) modified = true;
else if( m_ui.italicCheckBox->isChecked() != f.italic() ) modified = true;
// animations
else if( m_ui.animationsEnabled->isChecked() != m_internalSettings->animationsEnabled() ) modified = true;
else if( m_ui.animationsDuration->value() != m_internalSettings->animationsDuration() ) modified = true;
// shadows
else if( m_ui.shadowSize->currentIndex() != m_internalSettings->shadowSize() ) modified = true;
else if( qRound( qreal(m_ui.shadowStrength->value()*255)/100 ) != m_internalSettings->shadowStrength() ) modified = true;
else if( m_ui.shadowColor->color() != m_internalSettings->shadowColor() ) modified = true;
// exceptions
else if( m_ui.exceptions->isChanged() ) modified = true;
else {
int indx = m_ui.weightComboBox->currentIndex();
switch (indx) {
case 1:
if (f.weight() != QFont::Medium) modified = true;
break;
case 2:
if (f.weight() != QFont::DemiBold) modified = true;
break;
case 3:
if (f.weight() != QFont::Bold) modified = true;
break;
case 4:
if (f.weight() != QFont::ExtraBold) modified = true;
break;
case 5:
if (f.weight() != QFont::Black) modified = true;
break;
default:
if (f.bold()) modified = true;
break;
}
}
setChanged( modified );
}
//_______________________________________________
void ConfigWidget::setChanged( bool value )
{
emit changed( value );
}
}

View file

@ -0,0 +1,93 @@
#ifndef breezeconfigwidget_h
#define breezeconfigwidget_h
//////////////////////////////////////////////////////////////////////////////
// breezeconfigurationui.h
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "ui_breezeconfigurationui.h"
#include "breezeexceptionlistwidget.h"
#include "breezesettings.h"
#include "breeze.h"
#include <KCModule>
#include <KSharedConfig>
#include <QWidget>
#include <QSharedPointer>
namespace Breeze
{
//_____________________________________________
class ConfigWidget: public KCModule
{
Q_OBJECT
public:
//* constructor
explicit ConfigWidget( QWidget*, const QVariantList& );
//* destructor
virtual ~ConfigWidget() = default;
//* default
void defaults() override;
//* load configuration
void load() override;
//* save configuration
void save() override;
protected Q_SLOTS:
//* update changed state
virtual void updateChanged();
protected:
//* set changed state
void setChanged( bool );
private:
//* ui
Ui_BreezeConfigurationUI m_ui;
//* kconfiguration object
KSharedConfig::Ptr m_configuration;
//* internal exception
InternalSettingsPtr m_internalSettings;
//* changed state
bool m_changed;
};
}
#endif

View file

@ -0,0 +1,131 @@
[Desktop Entry]
Exec=kcmshell5 breezedecorationconfig
Icon=preferences-system-windows
Type=Service
X-KDE-ServiceTypes=KCModule
X-KDE-Library=org.kde.kdecoration2/breezedecoration
X-KDE-PluginKeyword=kcmodule
X-KDE-ParentApp=kcontrol
X-KDE-Weight=60
Name=Breeze Window Decoration
Name[ca]=Decoració de les finestres Brisa
Name[ca@valencia]=Decoració de les finestres Brisa
Name[cs]=Dekorace oken Breeze
Name[da]=Breeze vinduesdekoration
Name[de]=Breeze-Fensterdekoration
Name[el]=Διακοσμήσεις παραθύρου Breeze
Name[en_GB]=Breeze Window Decoration
Name[es]=Decoración de ventanas Brisa
Name[eu]=Breeze leihoen apaindura
Name[fi]=Breeze-ikkunankehys
Name[fr]=Décoration de fenêtre Brise
Name[gl]=Decoración de xanela de Breeze
Name[hu]=Breeze ablakdekoráció
Name[ia]=Decoration de fenestra Breeze
Name[id]=Dekorasi Jendela Breeze
Name[it]=Decorazione delle finestre di Brezza
Name[ko]=Breeze
Name[nl]=Breeze vensterdecoratie
Name[nn]=Breeze-vindaugsdekorasjon
Name[pl]=Wygląd okien Bryzy
Name[pt]=Decoração de Janelas Brisa
Name[pt_BR]=Decorações da janela Breeze
Name[ru]=Оформление окон Breeze
Name[sk]=Dekorácie okien Breeze
Name[sl]=Okraski oken Sapica
Name[sr]=Поветарчева декорација прозора
Name[sr@ijekavian]=Поветарчева декорација прозора
Name[sr@ijekavianlatin]=Povetarčeva dekoracija prozora
Name[sr@latin]=Povetarčeva dekoracija prozora
Name[sv]=Breeze fönsterdekoration
Name[tr]=Breeze Pencere Dekorasyonu
Name[uk]=Декорації вікон Breeze
Name[x-test]=xxBreeze Window Decorationxx
Name[zh_CN]=
Name[zh_TW]=Breeze
Comment=Modify the appearance of window decorations
Comment[ar]=عدّل مظهر زخرفات النّوافذ
Comment[ca]=Modifica l'aparença de les decoracions de les finestres
Comment[ca@valencia]=Modifica l'aparença de les decoracions de les finestres
Comment[cs]=Změnit vzhled dekorace oken
Comment[da]=Ændr vinduesdekorationers udseende
Comment[de]=Das Erscheinungsbild der Fensterdekorationen ändern
Comment[el]=Τροποποίηση εμφάνισης της διακόσμησης παραθύρου
Comment[en_GB]=Modify the appearance of window decorations
Comment[es]=Modificar el aspecto de las decoraciones de las ventanas
Comment[et]=Akna dekoratsioonide välimuse muutmine
Comment[eu]=Aldatu leiho apainduren itxura
Comment[fi]=Muuta ikkunoiden kehysten ulkoasua
Comment[fr]=Modifier l'apparence des décorations des fenêtres
Comment[gl]=Modifica a aparencia da decoración da xanela
Comment[he]=התאם את מראה מסגרות החלונות
Comment[hu]=Az ablakdekorációk megjelenésének módosítása
Comment[ia]=Modifica le apparentia de decorationes de fenestra
Comment[id]=Memodofikasi penampilan dekorasi jendela
Comment[it]=Modifica l'aspetto delle decorazioni delle finestre
Comment[ko]=
Comment[lt]=Keisti lango dekoracijų išvaizdą
Comment[nb]=Endre utseende for vindusdekorasjoner
Comment[nl]=Wijzig het uiterlijk van vensterdecoraties
Comment[nn]=Endra utsjånad på vindaugspynt
Comment[pl]=Zmień wygląd i wystrój okien
Comment[pt]=Modificar a aparência das decorações das janelas
Comment[pt_BR]=Modifica a aparência das decorações da janela
Comment[ro]=Modifică aspectul decorațiilor pentru ferestre
Comment[ru]=Настройка заголовков окон в стиле Breeze
Comment[sk]=Zmena vzhľadu dekorácie okien
Comment[sl]=Spremenite videz okraskov oken
Comment[sr]=Измените изглед декорација прозора
Comment[sr@ijekavian]=Измијените изглед декорација прозора
Comment[sr@ijekavianlatin]=Izmijenite izgled dekoracija prozora
Comment[sr@latin]=Izmenite izgled dekoracija prozora
Comment[sv]=Ändra utseendet hos fönsterdekorationer
Comment[tr]=Pencere dekorasyonlarının görünümünü değiştir
Comment[uk]=Зміна вигляду декорацій вікон
Comment[x-test]=xxModify the appearance of window decorationsxx
Comment[zh_CN]=
Comment[zh_TW]=
X-KDE-Keywords=breeze,decoration
X-KDE-Keywords[ar]=نسيم,زخرفة
X-KDE-Keywords[ca]=breeze,brisa,decoració
X-KDE-Keywords[ca@valencia]=breeze,brisa,decoració
X-KDE-Keywords[cs]=breeze,dekorace
X-KDE-Keywords[da]=breeze,dekoration
X-KDE-Keywords[de]=Breeze-Dekoration
X-KDE-Keywords[el]=breeze,διακόσμηση
X-KDE-Keywords[en_GB]=breeze,decoration
X-KDE-Keywords[es]=breeze,brisa,decoración
X-KDE-Keywords[et]=breeze,dekoratsioon
X-KDE-Keywords[eu]=breeze,apainketa,apaindura
X-KDE-Keywords[fi]=breeze,decoration,kehys,koriste,koristus,ikkunakehys
X-KDE-Keywords[fr]=breeze,décoration
X-KDE-Keywords[gl]=breeze,decoración
X-KDE-Keywords[he]=breeze,decoration,מסגרת
X-KDE-Keywords[hu]=breeze,dekoráció
X-KDE-Keywords[ia]=breeze,decoration
X-KDE-Keywords[id]=breeze,dekorasi
X-KDE-Keywords[it]=brezza,decorazione
X-KDE-Keywords[ko]=breeze,decoration,
X-KDE-Keywords[lt]=breeze,kekoracija
X-KDE-Keywords[nb]=beeze,dekorasjon
X-KDE-Keywords[nl]=breeze,decoratie
X-KDE-Keywords[nn]=breeze,dekorasjonar,pynt
X-KDE-Keywords[pl]=breeze,dekoracja,bryza,wystrój
X-KDE-Keywords[pt]=brisa,decoração
X-KDE-Keywords[pt_BR]=breeze,decoração
X-KDE-Keywords[ro]=briză,breeze,decorare,decorație
X-KDE-Keywords[ru]=breeze,бриз,decoration,декорации окон,оформление окон
X-KDE-Keywords[sk]=breeze,dekorácia
X-KDE-Keywords[sl]=sapica,okraski
X-KDE-Keywords[sr]=breeze,decoration,Поветарац,декорација
X-KDE-Keywords[sr@ijekavian]=breeze,decoration,Поветарац,декорација
X-KDE-Keywords[sr@ijekavianlatin]=breeze,decoration,Povetarac,dekoracija
X-KDE-Keywords[sr@latin]=breeze,decoration,Povetarac,dekoracija
X-KDE-Keywords[sv]=breeze,dekoration
X-KDE-Keywords[tr]=esinti,dekorasyon
X-KDE-Keywords[uk]=breeze,decoration,бриз,декорації,декорація,обрамлення
X-KDE-Keywords[x-test]=xxbreezexx,xxdecorationxx
X-KDE-Keywords[zh_CN]=breeze,decoration,,
X-KDE-Keywords[zh_TW]=breeze,decoration

View file

@ -0,0 +1,182 @@
//////////////////////////////////////////////////////////////////////////////
// breezedetectwidget.cpp
// Note: this class is a stripped down version of
// /kdebase/workspace/kwin/kcmkwin/kwinrules/detectwidget.cpp
// Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezedetectwidget.h"
#include "breeze.h"
#include <KWindowInfo>
#include <QPushButton>
#include <QMouseEvent>
#include <config-breeze.h>
#if BREEZE_HAVE_X11
#include <QX11Info>
#include <xcb/xcb.h>
#endif
namespace Breeze
{
//_________________________________________________________
DetectDialog::DetectDialog( QWidget* parent ):
QDialog( parent )
{
// setup
m_ui.setupUi( this );
connect( m_ui.buttonBox->button( QDialogButtonBox::Cancel ), SIGNAL(clicked()), this, SLOT(close()) );
m_ui.windowClassCheckBox->setChecked( true );
#if BREEZE_HAVE_X11
if (QX11Info::isPlatformX11()) {
// create atom
xcb_connection_t* connection( QX11Info::connection() );
const QString atomName( QStringLiteral( "WM_STATE" ) );
xcb_intern_atom_cookie_t cookie( xcb_intern_atom( connection, false, atomName.size(), qPrintable( atomName ) ) );
QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> reply( xcb_intern_atom_reply( connection, cookie, nullptr) );
m_wmStateAtom = reply ? reply->atom : 0;
}
#endif
}
//_________________________________________________________
void DetectDialog::detect( WId window )
{
if( window == 0 ) selectWindow();
else readWindow( window );
}
//_________________________________________________________
void DetectDialog::readWindow( WId window )
{
if( window == 0 )
{
emit detectionDone( false );
return;
}
m_info.reset(new KWindowInfo( window, NET::WMAllProperties, NET::WM2AllProperties ));
if( !m_info->valid())
{
emit detectionDone( false );
return;
}
const QString wmClassClass( QString::fromUtf8( m_info->windowClassClass() ) );
const QString wmClassName( QString::fromUtf8( m_info->windowClassName() ) );
m_ui.windowClass->setText( QStringLiteral( "%1 (%2 %3)" ).arg( wmClassClass ).arg( wmClassName ).arg( wmClassClass ) );
m_ui.windowTitle->setText( m_info->name() );
emit detectionDone( exec() == QDialog::Accepted );
}
//_________________________________________________________
void DetectDialog::selectWindow()
{
// use a dialog, so that all user input is blocked
// use WX11BypassWM and moving away so that it's not actually visible
// grab only mouse, so that keyboard can be used e.g. for switching windows
m_grabber = new QDialog( nullptr, Qt::X11BypassWindowManagerHint );
m_grabber->move( -1000, -1000 );
m_grabber->setModal( true );
m_grabber->show();
// need to explicitly override cursor for Qt5
qApp->setOverrideCursor( Qt::CrossCursor );
m_grabber->grabMouse( Qt::CrossCursor );
m_grabber->installEventFilter( this );
}
//_________________________________________________________
bool DetectDialog::eventFilter( QObject* o, QEvent* e )
{
// check object and event type
if( o != m_grabber ) return false;
if( e->type() != QEvent::MouseButtonRelease ) return false;
// need to explicitely release cursor for Qt5
qApp->restoreOverrideCursor();
// delete old m_grabber
delete m_grabber;
m_grabber = nullptr;
// check button
if( static_cast< QMouseEvent* >( e )->button() != Qt::LeftButton ) return true;
// read window information
readWindow( findWindow() );
return true;
}
//_________________________________________________________
WId DetectDialog::findWindow()
{
#if BREEZE_HAVE_X11
if (!QX11Info::isPlatformX11()) {
return 0;
}
// check atom
if( !m_wmStateAtom ) return 0;
xcb_connection_t* connection( QX11Info::connection() );
xcb_window_t parent( QX11Info::appRootWindow() );
// why is there a loop of only 10 here
for( int i = 0; i < 10; ++i )
{
// query pointer
xcb_query_pointer_cookie_t pointerCookie( xcb_query_pointer( connection, parent ) );
QScopedPointer<xcb_query_pointer_reply_t, QScopedPointerPodDeleter> pointerReply( xcb_query_pointer_reply( connection, pointerCookie, nullptr ) );
if( !( pointerReply && pointerReply->child ) ) return 0;
const xcb_window_t child( pointerReply->child );
xcb_get_property_cookie_t cookie( xcb_get_property( connection, 0, child, m_wmStateAtom, XCB_GET_PROPERTY_TYPE_ANY, 0, 0 ) );
QScopedPointer<xcb_get_property_reply_t, QScopedPointerPodDeleter> reply( xcb_get_property_reply( connection, cookie, nullptr ) );
if( reply && reply->type ) return child;
else parent = child;
}
#endif
return 0;
}
}

113
config/breezedetectwidget.h Normal file
View file

@ -0,0 +1,113 @@
#ifndef breezedetectwidget_h
#define breezedetectwidget_h
//////////////////////////////////////////////////////////////////////////////
// breezedetectwidget.h
// Note: this class is a stripped down version of
// /kdebase/workspace/kwin/kcmkwin/kwinrules/detectwidget.h
// Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezesettings.h"
#include "ui_breezedetectwidget.h"
#include <QByteArray>
#include <QCheckBox>
#include <QDialog>
#include <QEvent>
#include <QLabel>
#include <kwindowsystem.h>
namespace Breeze
{
class DetectDialog : public QDialog
{
Q_OBJECT
public:
//* constructor
explicit DetectDialog( QWidget* );
//* read window properties or select one from mouse grab
void detect( WId window );
//* selected class
QByteArray selectedClass() const;
//* window information
const KWindowInfo& windowInfo() const
{ return *(m_info.data()); }
//* exception type
InternalSettings::EnumExceptionType exceptionType() const
{
if( m_ui.windowClassCheckBox->isChecked() ) return InternalSettings::ExceptionWindowClassName;
else if( m_ui.windowTitleCheckBox->isChecked() ) return InternalSettings::ExceptionWindowTitle;
else return InternalSettings::ExceptionWindowClassName;
}
Q_SIGNALS:
void detectionDone( bool );
protected:
bool eventFilter( QObject* o, QEvent* e ) override;
private:
//* select window from grab
void selectWindow();
//* read window properties
void readWindow( WId window );
//* find window under cursor
WId findWindow();
//* execute
void executeDialog();
//* ui
Ui::BreezeDetectWidget m_ui;
//* invisible dialog used to grab mouse
QDialog* m_grabber = nullptr;
//* current window information
QScopedPointer<KWindowInfo> m_info;
//* wm state atom
quint32 m_wmStateAtom = 0;
};
} // namespace
#endif

View file

@ -0,0 +1,197 @@
//////////////////////////////////////////////////////////////////////////////
// breezeexceptiondialog.cpp
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezeexceptiondialog.h"
#include "breezedetectwidget.h"
#include "config-breeze.h"
#if BREEZE_HAVE_X11
#include <QX11Info>
#endif
namespace Breeze
{
//___________________________________________
ExceptionDialog::ExceptionDialog( QWidget* parent ):
QDialog( parent )
{
m_ui.setupUi( this );
connect( m_ui.buttonBox->button( QDialogButtonBox::Cancel ), SIGNAL(clicked()), this, SLOT(close()) );
// store checkboxes from ui into list
m_checkboxes.insert( BorderSize, m_ui.borderSizeCheckBox );
// detect window properties
connect( m_ui.detectDialogButton, SIGNAL(clicked()), SLOT(selectWindowProperties()) );
// connections
connect( m_ui.exceptionType, SIGNAL(currentIndexChanged(int)), SLOT(updateChanged()) );
connect( m_ui.exceptionEditor, SIGNAL(textChanged(QString)), SLOT(updateChanged()) );
connect( m_ui.borderSizeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateChanged()) );
for( CheckBoxMap::iterator iter = m_checkboxes.begin(); iter != m_checkboxes.end(); ++iter )
{ connect( iter.value(), SIGNAL(clicked()), SLOT(updateChanged()) ); }
connect( m_ui.hideTitleBar, SIGNAL(clicked()), SLOT(updateChanged()) );
connect( m_ui.opaqueTitleBar, SIGNAL(clicked()), SLOT(updateChanged()) );
m_ui.opacityOverrideLabelSpinBox->setSpecialValueText(tr("None"));
connect( m_ui.opacityOverrideLabelSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), [=](int /*i*/){updateChanged();} );
connect( m_ui.isDialog, SIGNAL(clicked()), SLOT(updateChanged()) );
// hide detection dialog on non X11 platforms
#if BREEZE_HAVE_X11
if( !QX11Info::isPlatformX11() ) m_ui.detectDialogButton->hide();
#else
m_ui.detectDialogButton->hide();
#endif
}
//___________________________________________
void ExceptionDialog::setException( InternalSettingsPtr exception )
{
// store exception internally
m_exception = exception;
// type
m_ui.exceptionType->setCurrentIndex(m_exception->exceptionType() );
m_ui.exceptionEditor->setText( m_exception->exceptionPattern() );
m_ui.borderSizeComboBox->setCurrentIndex( m_exception->borderSize() );
m_ui.hideTitleBar->setChecked( m_exception->hideTitleBar() );
m_ui.opaqueTitleBar->setChecked( m_exception->opaqueTitleBar() );
m_ui.opacityOverrideLabelSpinBox->setValue( m_exception->opacityOverride() );
m_ui.isDialog->setChecked( m_exception->isDialog() );
// mask
for( CheckBoxMap::iterator iter = m_checkboxes.begin(); iter != m_checkboxes.end(); ++iter )
{ iter.value()->setChecked( m_exception->mask() & iter.key() ); }
setChanged( false );
}
//___________________________________________
void ExceptionDialog::save()
{
m_exception->setExceptionType( m_ui.exceptionType->currentIndex() );
m_exception->setExceptionPattern( m_ui.exceptionEditor->text() );
m_exception->setBorderSize( m_ui.borderSizeComboBox->currentIndex() );
m_exception->setHideTitleBar( m_ui.hideTitleBar->isChecked() );
m_exception->setOpaqueTitleBar( m_ui.opaqueTitleBar->isChecked() );
m_exception->setOpacityOverride( m_ui.opacityOverrideLabelSpinBox->value() );
m_exception->setIsDialog( m_ui.isDialog->isChecked() );
// mask
unsigned int mask = None;
for( CheckBoxMap::iterator iter = m_checkboxes.begin(); iter != m_checkboxes.end(); ++iter )
{ if( iter.value()->isChecked() ) mask |= iter.key(); }
m_exception->setMask( mask );
setChanged( false );
}
//___________________________________________
void ExceptionDialog::updateChanged()
{
bool modified( false );
if( m_exception->exceptionType() != m_ui.exceptionType->currentIndex() ) modified = true;
else if( m_exception->exceptionPattern() != m_ui.exceptionEditor->text() ) modified = true;
else if( m_exception->borderSize() != m_ui.borderSizeComboBox->currentIndex() ) modified = true;
else if( m_exception->hideTitleBar() != m_ui.hideTitleBar->isChecked() ) modified = true;
else if( m_exception->opaqueTitleBar() != m_ui.opaqueTitleBar->isChecked() ) modified = true;
else if( m_exception->opacityOverride() != m_ui.opacityOverrideLabelSpinBox->value() ) modified = true;
else if( m_exception->isDialog() != m_ui.isDialog->isChecked() ) modified = true;
else
{
// check mask
for( CheckBoxMap::iterator iter = m_checkboxes.begin(); iter != m_checkboxes.end(); ++iter )
{
if( iter.value()->isChecked() != (bool)( m_exception->mask() & iter.key() ) )
{
modified = true;
break;
}
}
}
setChanged( modified );
}
//___________________________________________
void ExceptionDialog::selectWindowProperties()
{
// create widget
if( !m_detectDialog )
{
m_detectDialog = new DetectDialog( this );
connect( m_detectDialog, SIGNAL(detectionDone(bool)), SLOT(readWindowProperties(bool)) );
}
m_detectDialog->detect(0);
}
//___________________________________________
void ExceptionDialog::readWindowProperties( bool valid )
{
Q_CHECK_PTR( m_detectDialog );
if( valid )
{
// type
m_ui.exceptionType->setCurrentIndex( m_detectDialog->exceptionType() );
// window info
const KWindowInfo& info( m_detectDialog->windowInfo() );
switch( m_detectDialog->exceptionType() )
{
default:
case InternalSettings::ExceptionWindowClassName:
m_ui.exceptionEditor->setText( QString::fromUtf8( info.windowClassClass() ) );
break;
case InternalSettings::ExceptionWindowTitle:
m_ui.exceptionEditor->setText( info.name() );
break;
}
}
delete m_detectDialog;
m_detectDialog = nullptr;
}
}

View file

@ -0,0 +1,114 @@
#ifndef breezeexceptiondialog_h
#define breezeexceptiondialog_h
//////////////////////////////////////////////////////////////////////////////
// breezeexceptiondialog.h
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "ui_breezeexceptiondialog.h"
#include "breeze.h"
#include <QCheckBox>
#include <QMap>
namespace Breeze
{
class DetectDialog;
//* breeze exceptions list
class ExceptionDialog: public QDialog
{
Q_OBJECT
public:
//* constructor
explicit ExceptionDialog( QWidget* parent );
//* destructor
virtual ~ExceptionDialog()
{}
//* set exception
void setException( InternalSettingsPtr );
//* save exception
void save();
//* true if changed
virtual bool isChanged() const
{ return m_changed; }
Q_SIGNALS:
//* emmited when changed
void changed( bool );
protected:
//* set changed state
virtual void setChanged( bool value )
{
m_changed = value;
emit changed( value );
}
protected Q_SLOTS:
//* check whether configuration is changed and emit appropriate signal if yes
virtual void updateChanged();
private Q_SLOTS:
//* select window properties from grabbed pointers
void selectWindowProperties();
//* read properties of selected window
void readWindowProperties( bool );
private:
//* map mask and checkbox
using CheckBoxMap=QMap< ExceptionMask, QCheckBox*>;
Ui::BreezeExceptionDialog m_ui;
//* map mask and checkbox
CheckBoxMap m_checkboxes;
//* internal exception
InternalSettingsPtr m_exception;
//* detection dialog
DetectDialog* m_detectDialog = nullptr;
//* changed state
bool m_changed = false;
};
}
#endif

View file

@ -0,0 +1,345 @@
//////////////////////////////////////////////////////////////////////////////
// breezeexceptionlistwidget.cpp
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezeexceptionlistwidget.h"
#include "breezeexceptiondialog.h"
#include <KLocalizedString>
#include <QMessageBox>
#include <QPointer>
#include <QIcon>
//__________________________________________________________
namespace Breeze
{
//__________________________________________________________
ExceptionListWidget::ExceptionListWidget( QWidget* parent ):
QWidget( parent )
{
// ui
m_ui.setupUi( this );
// list
m_ui.exceptionListView->setAllColumnsShowFocus( true );
m_ui.exceptionListView->setRootIsDecorated( false );
m_ui.exceptionListView->setSortingEnabled( false );
m_ui.exceptionListView->setModel( &model() );
m_ui.exceptionListView->sortByColumn( ExceptionModel::ColumnType );
m_ui.exceptionListView->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Ignored ) );
m_ui.moveUpButton->setIcon( QIcon::fromTheme( QStringLiteral( "arrow-up" ) ) );
m_ui.moveDownButton->setIcon( QIcon::fromTheme( QStringLiteral( "arrow-down" ) ) );
m_ui.addButton->setIcon( QIcon::fromTheme( QStringLiteral( "list-add" ) ) );
m_ui.removeButton->setIcon( QIcon::fromTheme( QStringLiteral( "list-remove" ) ) );
m_ui.editButton->setIcon( QIcon::fromTheme( QStringLiteral( "edit-rename" ) ) );
connect( m_ui.addButton, SIGNAL(clicked()), SLOT(add()) );
connect( m_ui.editButton, SIGNAL(clicked()), SLOT(edit()) );
connect( m_ui.removeButton, SIGNAL(clicked()), SLOT(remove()) );
connect( m_ui.moveUpButton, SIGNAL(clicked()), SLOT(up()) );
connect( m_ui.moveDownButton, SIGNAL(clicked()), SLOT(down()) );
connect( m_ui.exceptionListView, SIGNAL(activated(QModelIndex)), SLOT(edit()) );
connect( m_ui.exceptionListView, SIGNAL(clicked(QModelIndex)), SLOT(toggle(QModelIndex)) );
connect( m_ui.exceptionListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(updateButtons()) );
updateButtons();
resizeColumns();
}
//__________________________________________________________
void ExceptionListWidget::setExceptions( const InternalSettingsList& exceptions )
{
model().set( exceptions );
resizeColumns();
setChanged( false );
}
//__________________________________________________________
InternalSettingsList ExceptionListWidget::exceptions()
{
return model().get();
setChanged( false );
}
//__________________________________________________________
void ExceptionListWidget::updateButtons()
{
bool hasSelection( !m_ui.exceptionListView->selectionModel()->selectedRows().empty() );
m_ui.removeButton->setEnabled( hasSelection );
m_ui.editButton->setEnabled( hasSelection );
m_ui.moveUpButton->setEnabled( hasSelection && !m_ui.exceptionListView->selectionModel()->isRowSelected( 0, QModelIndex() ) );
m_ui.moveDownButton->setEnabled( hasSelection && !m_ui.exceptionListView->selectionModel()->isRowSelected( model().rowCount()-1, QModelIndex() ) );
}
//_______________________________________________________
void ExceptionListWidget::add()
{
QPointer<ExceptionDialog> dialog = new ExceptionDialog( this );
dialog->setWindowTitle( i18n( "New Exception - Breeze Settings" ) );
InternalSettingsPtr exception( new InternalSettings() );
exception->load();
dialog->setException( exception );
// run dialog and check existence
if( !dialog->exec() )
{
delete dialog;
return;
}
dialog->save();
delete dialog;
// check exceptions
if( !checkException( exception ) ) return;
// create new item
model().add( exception );
setChanged( true );
// make sure item is selected
QModelIndex index( model().index( exception ) );
if( index != m_ui.exceptionListView->selectionModel()->currentIndex() )
{
m_ui.exceptionListView->selectionModel()->select( index, QItemSelectionModel::Clear|QItemSelectionModel::Select|QItemSelectionModel::Rows );
m_ui.exceptionListView->selectionModel()->setCurrentIndex( index, QItemSelectionModel::Current|QItemSelectionModel::Rows );
}
resizeColumns();
}
//_______________________________________________________
void ExceptionListWidget::edit()
{
// retrieve selection
QModelIndex current( m_ui.exceptionListView->selectionModel()->currentIndex() );
if( ! model().contains( current ) ) return;
InternalSettingsPtr exception( model().get( current ) );
// create dialog
QPointer<ExceptionDialog> dialog( new ExceptionDialog( this ) );
dialog->setWindowTitle( i18n( "Edit Exception - Breeze Settings" ) );
dialog->setException( exception );
// map dialog
if( !dialog->exec() )
{
delete dialog;
return;
}
// check modifications
if( !dialog->isChanged() ) return;
// retrieve exception
dialog->save();
delete dialog;
// check new exception validity
checkException( exception );
resizeColumns();
setChanged( true );
}
//_______________________________________________________
void ExceptionListWidget::remove()
{
// confirmation dialog
{
QMessageBox messageBox( QMessageBox::Question, i18n("Question - Breeze Settings" ), i18n("Remove selected exception?"), QMessageBox::Yes | QMessageBox::Cancel );
messageBox.button( QMessageBox::Yes )->setText( i18n("Remove") );
messageBox.setDefaultButton( QMessageBox::Cancel );
if( messageBox.exec() == QMessageBox::Cancel ) return;
}
// remove
model().remove( model().get( m_ui.exceptionListView->selectionModel()->selectedRows() ) );
resizeColumns();
updateButtons();
setChanged( true );
}
//_______________________________________________________
void ExceptionListWidget::toggle( const QModelIndex& index )
{
if( !model().contains( index ) ) return;
if( index.column() != ExceptionModel::ColumnEnabled ) return;
// get matching exception
InternalSettingsPtr exception( model().get( index ) );
exception->setEnabled( !exception->enabled() );
setChanged( true );
}
//_______________________________________________________
void ExceptionListWidget::up()
{
InternalSettingsList selection( model().get( m_ui.exceptionListView->selectionModel()->selectedRows() ) );
if( selection.empty() ) { return; }
// retrieve selected indexes in list and store in model
QModelIndexList selectedIndices( m_ui.exceptionListView->selectionModel()->selectedRows() );
InternalSettingsList selectedExceptions( model().get( selectedIndices ) );
InternalSettingsList currentException( model().get() );
InternalSettingsList newExceptions;
for( InternalSettingsList::const_iterator iter = currentException.constBegin(); iter != currentException.constEnd(); ++iter )
{
// check if new list is not empty, current index is selected and last index is not.
// if yes, move.
if(
!( newExceptions.empty() ||
selectedIndices.indexOf( model().index( *iter ) ) == -1 ||
selectedIndices.indexOf( model().index( newExceptions.back() ) ) != -1
) )
{
InternalSettingsPtr last( newExceptions.back() );
newExceptions.removeLast();
newExceptions.append( *iter );
newExceptions.append( last );
} else newExceptions.append( *iter );
}
model().set( newExceptions );
// restore selection
m_ui.exceptionListView->selectionModel()->select( model().index( selectedExceptions.front() ), QItemSelectionModel::Clear|QItemSelectionModel::Select|QItemSelectionModel::Rows );
for( InternalSettingsList::const_iterator iter = selectedExceptions.constBegin(); iter != selectedExceptions.constEnd(); ++iter )
{ m_ui.exceptionListView->selectionModel()->select( model().index( *iter ), QItemSelectionModel::Select|QItemSelectionModel::Rows ); }
setChanged( true );
}
//_______________________________________________________
void ExceptionListWidget::down()
{
InternalSettingsList selection( model().get( m_ui.exceptionListView->selectionModel()->selectedRows() ) );
if( selection.empty() )
{ return; }
// retrieve selected indexes in list and store in model
QModelIndexList selectedIndices( m_ui.exceptionListView->selectionModel()->selectedIndexes() );
InternalSettingsList selectedExceptions( model().get( selectedIndices ) );
InternalSettingsList currentExceptions( model().get() );
InternalSettingsList newExceptions;
InternalSettingsListIterator iter( currentExceptions );
iter.toBack();
while( iter.hasPrevious() )
{
InternalSettingsPtr current( iter.previous() );
// check if new list is not empty, current index is selected and last index is not.
// if yes, move.
if(
!( newExceptions.empty() ||
selectedIndices.indexOf( model().index( current ) ) == -1 ||
selectedIndices.indexOf( model().index( newExceptions.front() ) ) != -1
) )
{
InternalSettingsPtr first( newExceptions.front() );
newExceptions.removeFirst();
newExceptions.prepend( current );
newExceptions.prepend( first );
} else newExceptions.prepend( current );
}
model().set( newExceptions );
// restore selection
m_ui.exceptionListView->selectionModel()->select( model().index( selectedExceptions.front() ), QItemSelectionModel::Clear|QItemSelectionModel::Select|QItemSelectionModel::Rows );
for( InternalSettingsList::const_iterator iter = selectedExceptions.constBegin(); iter != selectedExceptions.constEnd(); ++iter )
{ m_ui.exceptionListView->selectionModel()->select( model().index( *iter ), QItemSelectionModel::Select|QItemSelectionModel::Rows ); }
setChanged( true );
}
//_______________________________________________________
void ExceptionListWidget::resizeColumns() const
{
m_ui.exceptionListView->resizeColumnToContents( ExceptionModel::ColumnEnabled );
m_ui.exceptionListView->resizeColumnToContents( ExceptionModel::ColumnType );
m_ui.exceptionListView->resizeColumnToContents( ExceptionModel::ColumnRegExp );
}
//_______________________________________________________
bool ExceptionListWidget::checkException( InternalSettingsPtr exception )
{
while( exception->exceptionPattern().isEmpty() || !QRegExp( exception->exceptionPattern() ).isValid() )
{
QMessageBox::warning( this, i18n( "Warning - Breeze Settings" ), i18n("Regular Expression syntax is incorrect") );
QPointer<ExceptionDialog> dialog( new ExceptionDialog( this ) );
dialog->setException( exception );
if( dialog->exec() == QDialog::Rejected )
{
delete dialog;
return false;
}
dialog->save();
delete dialog;
}
return true;
}
}

View file

@ -0,0 +1,124 @@
#ifndef breezeexceptionlistwidget_h
#define breezeexceptionlistwidget_h
//////////////////////////////////////////////////////////////////////////////
// breezeexceptionlistwidget.h
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "ui_breezeexceptionlistwidget.h"
#include "breezeexceptionmodel.h"
//* QDialog used to commit selected files
namespace Breeze
{
class ExceptionListWidget: public QWidget
{
//* Qt meta object
Q_OBJECT
public:
//* constructor
explicit ExceptionListWidget( QWidget* = nullptr );
//* set exceptions
void setExceptions( const InternalSettingsList& );
//* get exceptions
InternalSettingsList exceptions();
//* true if changed
virtual bool isChanged() const
{ return m_changed; }
Q_SIGNALS:
//* emitted when changed
void changed( bool );
protected:
//* model
const ExceptionModel& model() const
{ return m_model; }
//* model
ExceptionModel& model()
{ return m_model; }
protected Q_SLOTS:
//* update button states
virtual void updateButtons();
//* add
virtual void add();
//* edit
virtual void edit();
//* remove
virtual void remove();
//* toggle
virtual void toggle( const QModelIndex& );
//* move up
virtual void up();
//* move down
virtual void down();
protected:
//* resize columns
void resizeColumns() const;
//* check exception
bool checkException( InternalSettingsPtr );
//* set changed state
virtual void setChanged( bool value )
{
m_changed = value;
emit changed( value );
}
private:
//* model
ExceptionModel m_model;
//* ui
Ui_BreezeExceptionListWidget m_ui;
//* changed state
bool m_changed = false;
};
}
#endif

View file

@ -0,0 +1,107 @@
//////////////////////////////////////////////////////////////////////////////
// breezeexceptionmodel.cpp
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezeexceptionmodel.h"
#include <KLocalizedString>
namespace Breeze
{
//_______________________________________________
const QString ExceptionModel::m_columnTitles[ ExceptionModel::nColumns ] =
{
QStringLiteral( "" ),
i18n("Exception Type"),
i18n("Regular Expression")
};
//__________________________________________________________________
QVariant ExceptionModel::data( const QModelIndex& index, int role ) const
{
// check index, role and column
if( !index.isValid() ) return QVariant();
// retrieve associated file info
const InternalSettingsPtr& configuration( get(index) );
// return text associated to file and column
if( role == Qt::DisplayRole )
{
switch( index.column() )
{
case ColumnType:
{
switch( configuration->exceptionType() )
{
case InternalSettings::ExceptionWindowTitle:
return i18n( "Window Title" );
default:
case InternalSettings::ExceptionWindowClassName:
return i18n( "Window Class Name" );
}
}
case ColumnRegExp: return configuration->exceptionPattern();
default: return QVariant();
break;
}
} else if( role == Qt::CheckStateRole && index.column() == ColumnEnabled ) {
return configuration->enabled() ? Qt::Checked : Qt::Unchecked;
} else if( role == Qt::ToolTipRole && index.column() == ColumnEnabled ) {
return i18n("Enable/disable this exception");
}
return QVariant();
}
//__________________________________________________________________
QVariant ExceptionModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(
orientation == Qt::Horizontal &&
role == Qt::DisplayRole &&
section >= 0 &&
section < nColumns )
{ return m_columnTitles[section]; }
// return empty
return QVariant();
}
}

View file

@ -0,0 +1,81 @@
#ifndef breezeexceptionmodel_h
#define breezeexceptionmodel_h
//////////////////////////////////////////////////////////////////////////////
// breezeexceptionmodel.h
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezelistmodel.h"
#include "breezesettings.h"
#include "breeze.h"
namespace Breeze
{
//* qlistview for object counters
class ExceptionModel: public ListModel<InternalSettingsPtr>
{
public:
//* number of columns
enum { nColumns = 3 };
//* column type enumeration
enum ColumnType {
ColumnEnabled,
ColumnType,
ColumnRegExp
};
//*@name methods reimplemented from base class
//@{
//* return data for a given index
QVariant data(const QModelIndex &index, int role) const override;
//* header data
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
//* number of columns for a given index
int columnCount(const QModelIndex& ) const override
{ return nColumns; }
//@}
protected:
//* sort
void privateSort( int, Qt::SortOrder ) override
{}
private:
//* column titles
static const QString m_columnTitles[ nColumns ];
};
}
#endif

View file

@ -0,0 +1,68 @@
//////////////////////////////////////////////////////////////////////////////
// itemmodel.cpp
// -------------------
//
// Copyright (c) 2009-2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezeitemmodel.h"
namespace Breeze
{
//_______________________________________________________________
ItemModel::ItemModel( QObject* parent ):
QAbstractItemModel( parent )
{}
//____________________________________________________________
void ItemModel::sort( int column, Qt::SortOrder order )
{
// store column and order
m_sortColumn = column;
m_sortOrder = order;
// emit signals and call private methods
emit layoutAboutToBeChanged();
privateSort( column, order );
emit layoutChanged();
}
//____________________________________________________________
QModelIndexList ItemModel::indexes( int column, const QModelIndex& parent ) const
{
QModelIndexList out;
int rows( rowCount( parent ) );
for( int row = 0; row < rows; row++ )
{
QModelIndex index( this->index( row, column, parent ) );
if( !index.isValid() ) continue;
out.append( index );
out += indexes( column, index );
}
return out;
}
}

113
config/breezeitemmodel.h Normal file
View file

@ -0,0 +1,113 @@
#ifndef ItemModel_h
#define ItemModel_h
//////////////////////////////////////////////////////////////////////////////
// itemmodel.h
// -------------------
//
// Copyright (c) 2009-2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include <QAbstractItemModel>
namespace Breeze
{
//* Job model. Stores job information for display in lists
class ItemModel : public QAbstractItemModel
{
public:
//* constructor
explicit ItemModel(QObject *parent = nullptr);
//* destructor
virtual ~ItemModel()
{}
//* return all indexes in model starting from parent [recursive]
QModelIndexList indexes( int column = 0, const QModelIndex& parent = QModelIndex() ) const;
//*@name sorting
//@{
//* sort
virtual void sort()
{ sort( sortColumn(), sortOrder() ); }
//* sort
void sort( int column, Qt::SortOrder order = Qt::AscendingOrder ) override;
//* current sorting column
const int& sortColumn() const
{ return m_sortColumn; }
//* current sort order
const Qt::SortOrder& sortOrder() const
{ return m_sortOrder; }
//@}
protected:
//* this sort columns without calling the layout changed callbacks
void privateSort()
{ privateSort( m_sortColumn, m_sortOrder ); }
//* private sort, with no signals emmitted
virtual void privateSort( int column, Qt::SortOrder order ) = 0;
//* used to sort items in list
class SortFTor
{
public:
//* constructor
explicit SortFTor( const int& type, Qt::SortOrder order = Qt::AscendingOrder ):
_type( type ),
_order( order )
{}
protected:
//* column
int _type;
//* order
Qt::SortOrder _order;
};
private:
//* sorting column
int m_sortColumn = 0;
//* sorting order
Qt::SortOrder m_sortOrder = Qt::AscendingOrder;
};
}
#endif

365
config/breezelistmodel.h Normal file
View file

@ -0,0 +1,365 @@
#ifndef ListModel_h
#define ListModel_h
//////////////////////////////////////////////////////////////////////////////
// listmodel.h
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "breezeitemmodel.h"
#include <QSet>
#include <QList>
#include <algorithm>
namespace Breeze
{
//! Job model. Stores job information for display in lists
template<class T> class ListModel : public ItemModel
{
public:
//! value type
typedef T ValueType;
//! reference
typedef T& Reference;
//! pointer
typedef T* Pointer;
//! value list and iterators
typedef QList<ValueType> List;
typedef QListIterator<ValueType> ListIterator;
typedef QMutableListIterator<ValueType> MutableListIterator;
//! list of vector
// typedef QSet<ValueType> Set;
//! constructor
ListModel(QObject *parent = nullptr):
ItemModel( parent )
{}
//! destructor
virtual ~ListModel()
{}
//!@name methods reimplemented from base class
//@{
//! flags
Qt::ItemFlags flags(const QModelIndex &index) const override
{
if (!index.isValid()) return nullptr;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
//! unique index for given row, column and parent index
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
{
// check if index is valid
if( !hasIndex( row, column, parent ) ) return QModelIndex();
// return invalid index if parent is valid
if( parent.isValid() ) return QModelIndex();
// check against _values
return ( row < (int) _values.size() ) ? createIndex( row, column ):QModelIndex();
}
//! index of parent
QModelIndex parent(const QModelIndex &) const override
{ return QModelIndex(); }
//! number of rows below given index
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{ return parent.isValid() ? 0:_values.size(); }
//@}
//!@name selection
//@{
//! clear internal list selected items
virtual void clearSelectedIndexes()
{ _selection.clear(); }
//! store index internal selection state
virtual void setIndexSelected( const QModelIndex& index, bool value )
{
if( value ) _selection.push_back( get(index) );
else _selection.erase( std::remove( _selection.begin(), _selection.end(), get(index) ), _selection.end() );
}
//! get list of internal selected items
virtual QModelIndexList selectedIndexes() const
{
QModelIndexList out;
for( typename List::const_iterator iter = _selection.begin(); iter != _selection.end(); iter++ )
{
QModelIndex index( ListModel::index( *iter ) );
if( index.isValid() ) out.push_back( index );
}
return out;
}
//@}
//!@name interface
//@{
//! add value
virtual void add( const ValueType& value )
{
emit layoutAboutToBeChanged();
_add( value );
privateSort();
emit layoutChanged();
}
//! add values
virtual void add( const List& values )
{
// check if not empty
// this avoids sending useless signals
if( values.empty() ) return;
emit layoutAboutToBeChanged();
for( typename List::const_iterator iter = values.begin(); iter != values.end(); iter++ )
{ _add( *iter ); }
privateSort();
emit layoutChanged();
}
//! insert values
virtual void insert( const QModelIndex& index, const ValueType& value )
{
emit layoutAboutToBeChanged();
_insert( index, value );
emit layoutChanged();
}
//! insert values
virtual void insert( const QModelIndex& index, const List& values )
{
emit layoutAboutToBeChanged();
// need to loop in reverse order so that the "values" ordering is preserved
ListIterator iter( values );
iter.toBack();
while( iter.hasPrevious() )
{ _insert( index, iter.previous() ); }
emit layoutChanged();
}
//! insert values
virtual void replace( const QModelIndex& index, const ValueType& value )
{
if( !index.isValid() ) add( value );
else {
emit layoutAboutToBeChanged();
setIndexSelected( index, false );
_values[index.row()] = value;
setIndexSelected( index, true );
emit layoutChanged();
}
}
//! remove
virtual void remove( const ValueType& value )
{
emit layoutAboutToBeChanged();
_remove( value );
emit layoutChanged();
}
//! remove
virtual void remove( const List& values )
{
// check if not empty
// this avoids sending useless signals
if( values.empty() ) return;
emit layoutAboutToBeChanged();
for( typename List::const_iterator iter = values.begin(); iter != values.end(); iter++ )
{ _remove( *iter ); }
emit layoutChanged();
}
//! clear
virtual void clear()
{ set( List() ); }
//! update values from list
/*!
values that are not found in current are removed
new values are set to the end.
This is slower than the "set" method, but the selection is not cleared in the process
*/
virtual void update( List values )
{
emit layoutAboutToBeChanged();
// store values to be removed
List removed_values;
// update values that are common to both lists
for( typename List::iterator iter = _values.begin(); iter != _values.end(); iter++ )
{
// see if iterator is in list
typename List::iterator found_iter( std::find( values.begin(), values.end(), *iter ) );
if( found_iter == values.end() ) removed_values.push_back( *iter );
else {
*iter = *found_iter;
values.erase( found_iter );
}
}
// remove values that have not been found in new list
for( typename List::const_iterator iter = removed_values.constBegin(); iter != removed_values.constEnd(); iter++ )
{ _remove( *iter ); }
// add remaining values
for( typename List::const_iterator iter = values.constBegin(); iter != values.constEnd(); iter++ )
{ _add( *iter ); }
privateSort();
emit layoutChanged();
}
//! set all values
virtual void set( const List& values )
{
emit layoutAboutToBeChanged();
_values = values;
_selection.clear();
privateSort();
emit layoutChanged();
}
//! return all values
const List& get() const
{ return _values; }
//! return value for given index
virtual ValueType get( const QModelIndex& index ) const
{ return (index.isValid() && index.row() < int(_values.size()) ) ? _values[index.row()]:ValueType(); }
//! return value for given index
virtual ValueType& get( const QModelIndex& index )
{
Q_ASSERT( index.isValid() && index.row() < int( _values.size() ) );
return _values[index.row()];
}
//! return all values
List get( const QModelIndexList& indexes ) const
{
List out;
for( QModelIndexList::const_iterator iter = indexes.begin(); iter != indexes.end(); iter++ )
{ if( iter->isValid() && iter->row() < int(_values.size()) ) out.push_back( get( *iter ) ); }
return out;
}
//! return index associated to a given value
virtual QModelIndex index( const ValueType& value, int column = 0 ) const
{
for( int row = 0; row < _values.size(); ++row )
{ if( value == _values[row] ) return index( row, column ); }
return QModelIndex();
}
//@}
//! return true if model contains given index
virtual bool contains( const QModelIndex& index ) const
{ return index.isValid() && index.row() < _values.size(); }
protected:
//! return all values
List& _get()
{ return _values; }
//! add, without update
virtual void _add( const ValueType& value )
{
typename List::iterator iter = std::find( _values.begin(), _values.end(), value );
if( iter == _values.end() ) _values.push_back( value );
else *iter = value;
}
//! add, without update
virtual void _insert( const QModelIndex& index, const ValueType& value )
{
if( !index.isValid() ) add( value );
int row = 0;
typename List::iterator iter( _values.begin() );
for( ;iter != _values.end() && row != index.row(); iter++, row++ )
{}
_values.insert( iter, value );
}
//! remove, without update
virtual void _remove( const ValueType& value )
{
_values.erase( std::remove( _values.begin(), _values.end(), value ), _values.end() );
_selection.erase( std::remove( _selection.begin(), _selection.end(), value ), _selection.end() );
}
private:
//! values
List _values;
//! selection
List _selection;
};
}
#endif

View file

@ -0,0 +1,552 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BreezeConfigurationUI</class>
<widget class="QWidget" name="BreezeConfigurationUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>428</width>
<height>418</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>General</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Tit&amp;le alignment:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>titleAlignment</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="titleAlignment">
<item>
<property name="text">
<string>Left</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text">
<string>Center (Full Width)</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>B&amp;utton size:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>buttonSize</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="buttonSize">
<item>
<property name="text">
<string>Tiny</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Button size:">Small</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Button size:">Medium</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Button size:">Large</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Button size:">Very Large</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QCheckBox" name="drawBorderOnMaximizedWindows">
<property name="text">
<string>Allow resizing maximized windows from window edges</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="3">
<widget class="QCheckBox" name="drawSizeGrip">
<property name="text">
<string>Add handle to resize windows with no border</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="3">
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,0,0,1">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Opacity:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="opacitySpinBox">
<property name="suffix">
<string> %</string>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="3">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Font:</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" colspan="5">
<widget class="QFontComboBox" name="fontComboBox"/>
</item>
<item row="1" column="1" alignment="Qt::AlignLeft">
<widget class="QSpinBox" name="fontSizeSpinBox">
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>50</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item row="1" column="4">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Weight:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="weightComboBox">
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Normal</string>
</property>
</item>
<item>
<property name="text">
<string>Medium</string>
</property>
</item>
<item>
<property name="text">
<string>DemiBold</string>
</property>
</item>
<item>
<property name="text">
<string>Bold</string>
</property>
</item>
<item>
<property name="text">
<string>ExtraBold</string>
</property>
</item>
<item>
<property name="text">
<string>Black</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="italicCheckBox">
<property name="text">
<string>Italic</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="9" column="0" colspan="3">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Animations</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="1" column="0">
<widget class="QLabel" name="animationsDurationLabel">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Anima&amp;tions duration:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>animationsDuration</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="animationsDuration">
<property name="enabled">
<bool>false</bool>
</property>
<property name="suffix">
<string> ms</string>
</property>
<property name="maximum">
<number>500</number>
</property>
</widget>
</item>
<item row="1" column="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="3">
<widget class="QCheckBox" name="animationsEnabled">
<property name="text">
<string>Enable animations</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>Shadows</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Si&amp;ze:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>shadowSize</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="shadowSize">
<item>
<property name="text">
<string comment="@item:inlistbox Button size:">None</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Button size:">Small</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Button size:">Medium</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Button size:">Large</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Button size:">Very Large</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string comment="strength of the shadow (from transparent to opaque)">S&amp;trength:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>shadowStrength</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="shadowStrength">
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="1" column="2">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Color:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KColorButton" name="shadowColor"/>
</item>
<item row="3" column="0" colspan="3">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Window-Specific Overrides</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="Breeze::ExceptionListWidget" name="exceptions" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KColorButton</class>
<extends>QPushButton</extends>
<header>kcolorbutton.h</header>
</customwidget>
<customwidget>
<class>Breeze::ExceptionListWidget</class>
<extends>QWidget</extends>
<header>config/breezeexceptionlistwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>titleAlignment</tabstop>
<tabstop>buttonSize</tabstop>
<tabstop>drawBorderOnMaximizedWindows</tabstop>
<tabstop>drawSizeGrip</tabstop>
<tabstop>animationsEnabled</tabstop>
<tabstop>animationsDuration</tabstop>
<tabstop>shadowSize</tabstop>
<tabstop>shadowStrength</tabstop>
<tabstop>shadowColor</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>animationsEnabled</sender>
<signal>toggled(bool)</signal>
<receiver>animationsDurationLabel</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>34</x>
<y>194</y>
</hint>
<hint type="destinationlabel">
<x>84</x>
<y>221</y>
</hint>
</hints>
</connection>
<connection>
<sender>animationsEnabled</sender>
<signal>toggled(bool)</signal>
<receiver>animationsDuration</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>108</x>
<y>194</y>
</hint>
<hint type="destinationlabel">
<x>141</x>
<y>229</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BreezeDetectWidget</class>
<widget class="QDialog" name="BreezeDetectWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>239</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Information about Selected Window</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Class: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="windowClass">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Title: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="windowTitle">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Window Property Selection</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="windowClassCheckBox">
<property name="text">
<string>Use window class (whole application)</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="windowTitleCheckBox">
<property name="locale">
<locale language="French" country="France"/>
</property>
<property name="text">
<string>Use window title</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>BreezeDetectWidget</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BreezeDetectWidget</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,333 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BreezeExceptionDialog</class>
<widget class="QDialog" name="BreezeExceptionDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>362</width>
<height>321</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Window Identification</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Matching window property: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>exceptionType</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Regular expression &amp;to match: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>exceptionEditor</cstring>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="detectDialogButton">
<property name="text">
<string>Detect Window Properties</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QLineEdit" name="exceptionEditor">
<property name="showClearButton" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="exceptionType">
<item>
<property name="text">
<string>Window Class Name</string>
</property>
</item>
<item>
<property name="text">
<string>Window Title</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Decoration Options</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="borderSizeCheckBox">
<property name="text">
<string>Border size:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="borderSizeComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<item>
<property name="text">
<string comment="@item:inlistbox Border size:">No Border</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Border size:">No Side Borders</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Border size:">Tiny</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Border size:">Normal</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Border size:">Large</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Border size:">Very Large</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Border size:">Huge</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Border size:">Very Huge</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Border size:">Oversized</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="isDialog">
<property name="text">
<string>Only for dialogs</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="hideTitleBar">
<property name="text">
<string>Hide window title bar</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="opaqueTitleBar">
<property name="text">
<string>Opaque title bar</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>16</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="opacityOverrideLabel">
<property name="text">
<string>Override opacity:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="opacityOverrideLabelSpinBox">
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>-1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>-1</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>BreezeExceptionDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>252</x>
<y>342</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BreezeExceptionDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>320</x>
<y>342</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>borderSizeCheckBox</sender>
<signal>toggled(bool)</signal>
<receiver>borderSizeComboBox</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>125</x>
<y>162</y>
</hint>
<hint type="destinationlabel">
<x>316</x>
<y>163</y>
</hint>
</hints>
</connection>
<connection>
<sender>opaqueTitleBar</sender>
<signal>toggled(bool)</signal>
<receiver>opacityOverrideLabel</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>104</x>
<y>202</y>
</hint>
<hint type="destinationlabel">
<x>82</x>
<y>229</y>
</hint>
</hints>
</connection>
<connection>
<sender>opaqueTitleBar</sender>
<signal>toggled(bool)</signal>
<receiver>opacityOverrideLabelSpinBox</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>104</x>
<y>202</y>
</hint>
<hint type="destinationlabel">
<x>166</x>
<y>229</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BreezeExceptionListWidget</class>
<widget class="QWidget" name="BreezeExceptionListWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>473</width>
<height>182</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0" rowspan="6">
<widget class="QTreeView" name="exceptionListView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
</widget>
</item>
<item row="5" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="moveUpButton">
<property name="text">
<string>Move Up</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="moveDownButton">
<property name="text">
<string>Move Down</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="addButton">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="removeButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="editButton">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>exceptionListView</tabstop>
<tabstop>moveUpButton</tabstop>
<tabstop>moveDownButton</tabstop>
<tabstop>addButton</tabstop>
<tabstop>removeButton</tabstop>
<tabstop>editButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,29 @@
################# dependencies #################
### Qt/KDE
find_package(Qt5 REQUIRED CONFIG COMPONENTS Widgets)
################# configuration #################
configure_file(config-breezecommon.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-breezecommon.h )
################# breezestyle target #################
set(breeze10common_LIB_SRCS
breezeboxshadowrenderer.cpp
)
add_library(breeze10common5 ${breeze10common_LIB_SRCS})
generate_export_header(breeze10common5
BASE_NAME breezecommon
EXPORT_FILE_NAME breezecommon_export.h)
target_link_libraries(breeze10common5
PUBLIC
Qt5::Core
Qt5::Gui)
set_target_properties(breeze10common5 PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
install(TARGETS breeze10common5 ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP)

View file

@ -0,0 +1,358 @@
/*
* Copyright (C) 2018 Vlad Zagorodniy <vladzzag@gmail.com>
*
* The box blur implementation is based on AlphaBoxBlur from Firefox.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// own
#include "breezeboxshadowrenderer.h"
// auto-generated
#include "config-breezecommon.h"
// Qt
#include <QPainter>
#include <QtMath>
namespace Breeze
{
static inline int calculateBlurRadius(qreal stdDev)
{
// See https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
const qreal gaussianScaleFactor = (3.0 * qSqrt(2.0 * M_PI) / 4.0) * 1.5;
return qMax(2, qFloor(stdDev * gaussianScaleFactor + 0.5));
}
static inline qreal calculateBlurStdDev(int radius)
{
// See https://www.w3.org/TR/css-backgrounds-3/#shadow-blur
return radius * 0.5;
}
static inline QSize calculateBlurExtent(int radius)
{
const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
return QSize(blurRadius, blurRadius);
}
struct BoxLobes
{
int left; ///< how many pixels sample to the left
int right; ///< how many pixels sample to the right
};
/**
* Compute box filter parameters.
*
* @param radius The blur radius.
* @returns Parameters for three box filters.
**/
static QVector<BoxLobes> computeLobes(int radius)
{
const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
const int z = blurRadius / 3;
int major;
int minor;
int final;
switch (blurRadius % 3) {
case 0:
major = z;
minor = z;
final = z;
break;
case 1:
major = z + 1;
minor = z;
final = z;
break;
case 2:
major = z + 1;
minor = z;
final = z + 1;
break;
default:
Q_UNREACHABLE();
break;
}
Q_ASSERT(major + minor + final == blurRadius);
return {
{major, minor},
{minor, major},
{final, final}
};
}
/**
* Process a row with a box filter.
*
* @param src The start of the row.
* @param dst The destination.
* @param width The width of the row, in pixels.
* @param horizontalStride The number of bytes from one alpha value to the
* next alpha value.
* @param verticalStride The number of bytes from one row to the next row.
* @param lobes Params of the box filter.
* @param transposeInput Whether the input is transposed.
* @param transposeOutput Whether the output should be transposed.
**/
static inline void boxBlurRowAlpha(const uint8_t *src, uint8_t *dst, int width, int horizontalStride,
int verticalStride, const BoxLobes &lobes, bool transposeInput,
bool transposeOutput)
{
const int inputStep = transposeInput ? verticalStride : horizontalStride;
const int outputStep = transposeOutput ? verticalStride : horizontalStride;
const int boxSize = lobes.left + 1 + lobes.right;
const int reciprocal = (1 << 24) / boxSize;
uint32_t alphaSum = (boxSize + 1) / 2;
const uint8_t *left = src;
const uint8_t *right = src;
uint8_t *out = dst;
const uint8_t firstValue = src[0];
const uint8_t lastValue = src[(width - 1) * inputStep];
alphaSum += firstValue * lobes.left;
const uint8_t *initEnd = src + (boxSize - lobes.left) * inputStep;
while (right < initEnd) {
alphaSum += *right;
right += inputStep;
}
const uint8_t *leftEnd = src + boxSize * inputStep;
while (right < leftEnd) {
*out = (alphaSum * reciprocal) >> 24;
alphaSum += *right - firstValue;
right += inputStep;
out += outputStep;
}
const uint8_t *centerEnd = src + width * inputStep;
while (right < centerEnd) {
*out = (alphaSum * reciprocal) >> 24;
alphaSum += *right - *left;
left += inputStep;
right += inputStep;
out += outputStep;
}
const uint8_t *rightEnd = dst + width * outputStep;
while (out < rightEnd) {
*out = (alphaSum * reciprocal) >> 24;
alphaSum += lastValue - *left;
left += inputStep;
out += outputStep;
}
}
/**
* Blur the alpha channel of a given image.
*
* @param image The input image.
* @param radius The blur radius.
* @param rect Specifies what part of the image to blur. If nothing is provided, then
* the whole alpha channel of the input image will be blurred.
**/
static inline void boxBlurAlpha(QImage &image, int radius, const QRect &rect = {})
{
if (radius < 2) {
return;
}
const QVector<BoxLobes> lobes = computeLobes(radius);
const QRect blurRect = rect.isNull() ? image.rect() : rect;
const int alphaOffset = QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3;
const int width = blurRect.width();
const int height = blurRect.height();
const int rowStride = image.bytesPerLine();
const int pixelStride = image.depth() >> 3;
const int bufferStride = qMax(width, height) * pixelStride;
QScopedPointer<uint8_t, QScopedPointerArrayDeleter<uint8_t> > buf(new uint8_t[2 * bufferStride]);
uint8_t *buf1 = buf.data();
uint8_t *buf2 = buf1 + bufferStride;
// Blur the image in horizontal direction.
for (int i = 0; i < height; ++i) {
uint8_t *row = image.scanLine(blurRect.y() + i) + blurRect.x() * pixelStride + alphaOffset;
boxBlurRowAlpha(row, buf1, width, pixelStride, rowStride, lobes[0], false, false);
boxBlurRowAlpha(buf1, buf2, width, pixelStride, rowStride, lobes[1], false, false);
boxBlurRowAlpha(buf2, row, width, pixelStride, rowStride, lobes[2], false, false);
}
// Blur the image in vertical direction.
for (int i = 0; i < width; ++i) {
uint8_t *column = image.scanLine(blurRect.y()) + (blurRect.x() + i) * pixelStride + alphaOffset;
boxBlurRowAlpha(column, buf1, height, pixelStride, rowStride, lobes[0], true, false);
boxBlurRowAlpha(buf1, buf2, height, pixelStride, rowStride, lobes[1], false, false);
boxBlurRowAlpha(buf2, column, height, pixelStride, rowStride, lobes[2], false, true);
}
}
static inline void mirrorTopLeftQuadrant(QImage &image)
{
const int width = image.width();
const int height = image.height();
const int centerX = qCeil(width * 0.5);
const int centerY = qCeil(height * 0.5);
const int alphaOffset = QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3;
const int stride = image.depth() >> 3;
for (int y = 0; y < centerY; ++y) {
uint8_t *in = image.scanLine(y) + alphaOffset;
uint8_t *out = in + (width - 1) * stride;
for (int x = 0; x < centerX; ++x, in += stride, out -= stride) {
*out = *in;
}
}
for (int y = 0; y < centerY; ++y) {
const uint8_t *in = image.scanLine(y) + alphaOffset;
uint8_t *out = image.scanLine(width - y - 1) + alphaOffset;
for (int x = 0; x < width; ++x, in += stride, out += stride) {
*out = *in;
}
}
}
static void renderShadow(QPainter *painter, const QRect &rect, qreal borderRadius, const QPoint &offset, int radius, const QColor &color)
{
const QSize inflation = calculateBlurExtent(radius);
const QSize size = rect.size() + 2 * inflation;
const qreal dpr = painter->device()->devicePixelRatioF();
QImage shadow(size * dpr, QImage::Format_ARGB32_Premultiplied);
shadow.setDevicePixelRatio(dpr);
shadow.fill(Qt::transparent);
QRect boxRect(QPoint(0, 0), rect.size());
boxRect.moveCenter(QRect(QPoint(0, 0), size).center());
const qreal xRadius = 2.0 * borderRadius / boxRect.width();
const qreal yRadius = 2.0 * borderRadius / boxRect.height();
QPainter shadowPainter;
shadowPainter.begin(&shadow);
shadowPainter.setRenderHint(QPainter::Antialiasing);
shadowPainter.setPen(Qt::NoPen);
shadowPainter.setBrush(Qt::black);
shadowPainter.drawRoundedRect(boxRect, xRadius, yRadius);
shadowPainter.end();
// Because the shadow texture is symmetrical, that's enough to blur
// only the top-left quadrant and then mirror it.
const QRect blurRect(0, 0, qCeil(shadow.width() * 0.5), qCeil(shadow.height() * 0.5));
const int scaledRadius = qRound(radius * dpr);
boxBlurAlpha(shadow, scaledRadius, blurRect);
mirrorTopLeftQuadrant(shadow);
// Give the shadow a tint of the desired color.
shadowPainter.begin(&shadow);
shadowPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
shadowPainter.fillRect(shadow.rect(), color);
shadowPainter.end();
// Actually, present the shadow.
QRect shadowRect = shadow.rect();
shadowRect.setSize(shadowRect.size() / dpr);
shadowRect.moveCenter(rect.center() + offset);
painter->drawImage(shadowRect, shadow);
}
void BoxShadowRenderer::setBoxSize(const QSize &size)
{
m_boxSize = size;
}
void BoxShadowRenderer::setBorderRadius(qreal radius)
{
m_borderRadius = radius;
}
void BoxShadowRenderer::setDevicePixelRatio(qreal dpr)
{
m_dpr = dpr;
}
void BoxShadowRenderer::addShadow(const QPoint &offset, int radius, const QColor &color)
{
Shadow shadow = {};
shadow.offset = offset;
shadow.radius = radius;
shadow.color = color;
m_shadows.append(shadow);
}
QImage BoxShadowRenderer::render() const
{
if (m_shadows.isEmpty()) {
return {};
}
QSize canvasSize;
for (const Shadow &shadow : qAsConst(m_shadows)) {
canvasSize = canvasSize.expandedTo(
calculateMinimumShadowTextureSize(m_boxSize, shadow.radius, shadow.offset));
}
QImage canvas(canvasSize * m_dpr, QImage::Format_ARGB32_Premultiplied);
canvas.setDevicePixelRatio(m_dpr);
canvas.fill(Qt::transparent);
QRect boxRect(QPoint(0, 0), m_boxSize);
boxRect.moveCenter(QRect(QPoint(0, 0), canvasSize).center());
QPainter painter(&canvas);
for (const Shadow &shadow : qAsConst(m_shadows)) {
renderShadow(&painter, boxRect, m_borderRadius, shadow.offset, shadow.radius, shadow.color);
}
painter.end();
return canvas;
}
QSize BoxShadowRenderer::calculateMinimumBoxSize(int radius)
{
const QSize blurExtent = calculateBlurExtent(radius);
return 2 * blurExtent + QSize(1, 1);
}
QSize BoxShadowRenderer::calculateMinimumShadowTextureSize(const QSize &boxSize, int radius, const QPoint &offset)
{
return boxSize + 2 * calculateBlurExtent(radius) + QSize(qAbs(offset.x()), qAbs(offset.y()));
}
} // namespace Breeze

View file

@ -0,0 +1,105 @@
/*
* Copyright (C) 2018 Vlad Zagorodniy <vladzzag@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
// own
#include "breezecommon_export.h"
// Qt
#include <QColor>
#include <QImage>
#include <QPoint>
#include <QSize>
namespace Breeze
{
class BREEZECOMMON_EXPORT BoxShadowRenderer
{
public:
// Compiler generated constructors & destructor are fine.
/**
* Set the size of the box.
* @param size The size of the box.
**/
void setBoxSize(const QSize &size);
/**
* Set the radius of box' corners.
* @param radius The border radius, in pixels.
**/
void setBorderRadius(qreal radius);
/**
* Set the device pixel ratio of the resulting shadow texture.
* @param dpr The device pixel ratio.
**/
void setDevicePixelRatio(qreal dpr);
/**
* Add a shadow.
* @param offset The offset of the shadow.
* @param radius The blur radius.
* @param color The color of the shadow.
**/
void addShadow(const QPoint &offset, int radius, const QColor &color);
/**
* Render the shadow.
**/
QImage render() const;
/**
* Calculate the minimum size of the box.
*
* This helper computes the minimum size of the box so the shadow behind it has
* full its strength.
*
* @param radius The blur radius of the shadow.
**/
static QSize calculateMinimumBoxSize(int radius);
/**
* Calculate the minimum size of the shadow texture.
*
* This helper computes the minimum size of the resulting texture so the shadow
* is not clipped.
*
* @param boxSize The size of the box.
* @param radius The blur radius.
* @param offset The offset of the shadow.
**/
static QSize calculateMinimumShadowTextureSize(const QSize &boxSize, int radius, const QPoint &offset);
private:
QSize m_boxSize;
qreal m_borderRadius = 0.0;
qreal m_dpr = 1.0;
struct Shadow {
QPoint offset;
int radius;
QColor color;
};
QVector<Shadow> m_shadows;
};
} // namespace Breeze

View file

@ -0,0 +1,28 @@
/* config-breezecommon.h. Generated by cmake from config-breezecommon.h.cmake */
/*************************************************************************
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
*************************************************************************/
#ifndef config_breeze_common_h
#define config_breeze_common_h
/* Define to 1 if breeze is compiled against KDE4 */
#cmakedefine01 BREEZE_COMMON_USE_KDE4
#endif

BIN
screenshots/Desktop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 KiB

BIN
screenshots/Settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB