diff --git a/plugins/kdecorations/aurorae/src/aurorae.cpp b/plugins/kdecorations/aurorae/src/aurorae.cpp index ae3050ca9..76ce9b0e7 100644 --- a/plugins/kdecorations/aurorae/src/aurorae.cpp +++ b/plugins/kdecorations/aurorae/src/aurorae.cpp @@ -1,734 +1,758 @@ /******************************************************************** Copyright (C) 2009, 2010, 2012 Martin Gräßlin 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, see . *********************************************************************/ #include "aurorae.h" #include "auroraetheme.h" #include "config-kwin.h" #include "kwineffectquickview.h" // qml imports #include "decorationoptions.h" // KDecoration2 #include #include #include // KDE #include #include #include #include #include #include #include #include #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(AuroraeDecoFactory, "aurorae.json", registerPlugin(); registerPlugin(QStringLiteral("themes")); registerPlugin(QStringLiteral("kcmodule")); ) namespace Aurorae { class Helper { public: void ref(); void unref(); QQmlComponent *component(const QString &theme); QQmlContext *rootContext(); QQmlComponent *svgComponent() { return m_svgComponent.data(); } static Helper &instance(); private: Helper() = default; void init(); QQmlComponent *loadComponent(const QString &themeName); int m_refCount = 0; QScopedPointer m_engine; QHash m_components; QScopedPointer m_svgComponent; }; Helper &Helper::instance() { static Helper s_helper; return s_helper; } void Helper::ref() { m_refCount++; if (m_refCount == 1) { m_engine.reset(new QQmlEngine); init(); } } void Helper::unref() { m_refCount--; if (m_refCount == 0) { // cleanup m_svgComponent.reset(); m_engine.reset(); m_components.clear(); } } static const QString s_defaultTheme = QStringLiteral("kwin4_decoration_qml_plastik"); static const QString s_qmlPackageFolder = QStringLiteral(KWIN_NAME "/decorations/"); /* * KDecoration2::BorderSize doesn't map to the indices used for the Aurorae SVG Button Sizes. * BorderSize defines None and NoSideBorder as index 0 and 1. These do not make sense for Button * Size, thus we need to perform a mapping between the enum value and the config value. */ static const int s_indexMapper = 2; QQmlComponent *Helper::component(const QString &themeName) { // maybe it's an SVG theme? if (themeName.startsWith(QLatin1String("__aurorae__svg__"))) { if (m_svgComponent.isNull()) { /* use logic from KDeclarative::setupBindings(): "addImportPath adds the path at the beginning, so to honour user's paths we need to traverse the list in reverse order" */ QStringListIterator paths(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("module/imports"), QStandardPaths::LocateDirectory)); paths.toBack(); while (paths.hasPrevious()) { m_engine->addImportPath(paths.previous()); } m_svgComponent.reset(new QQmlComponent(m_engine.data())); m_svgComponent->loadUrl(QUrl(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/aurorae/aurorae.qml")))); } // verify that the theme exists if (!QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("aurorae/themes/%1/%1rc").arg(themeName.mid(16))).isEmpty()) { return m_svgComponent.data(); } } // try finding the QML package auto it = m_components.constFind(themeName); if (it != m_components.constEnd()) { return it.value(); } auto component = loadComponent(themeName); if (component) { m_components.insert(themeName, component); return component; } // try loading default component if (themeName != s_defaultTheme) { return loadComponent(s_defaultTheme); } return nullptr; } QQmlComponent *Helper::loadComponent(const QString &themeName) { qCDebug(AURORAE) << "Trying to load QML Decoration " << themeName; const QString internalname = themeName.toLower(); const auto offers = KPackage::PackageLoader::self()->findPackages(QStringLiteral("KWin/Decoration"), s_qmlPackageFolder, [internalname] (const KPluginMetaData &data) { return data.pluginId().compare(internalname, Qt::CaseInsensitive) == 0; } ); if (offers.isEmpty()) { qCCritical(AURORAE) << "Couldn't find QML Decoration " << themeName; // TODO: what to do in error case? return nullptr; } const KPluginMetaData &service = offers.first(); const QString pluginName = service.pluginId(); const QString scriptName = service.value(QStringLiteral("X-Plasma-MainScript")); const QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, s_qmlPackageFolder + pluginName + QLatin1String("/contents/") + scriptName); if (file.isNull()) { qCDebug(AURORAE) << "Could not find script file for " << pluginName; // TODO: what to do in error case? return nullptr; } // setup the QML engine /* use logic from KDeclarative::setupBindings(): "addImportPath adds the path at the beginning, so to honour user's paths we need to traverse the list in reverse order" */ QStringListIterator paths(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("module/imports"), QStandardPaths::LocateDirectory)); paths.toBack(); while (paths.hasPrevious()) { m_engine->addImportPath(paths.previous()); } QQmlComponent *component = new QQmlComponent(m_engine.data(), m_engine.data()); component->loadUrl(QUrl::fromLocalFile(file)); return component; } QQmlContext *Helper::rootContext() { return m_engine->rootContext(); } void Helper::init() { // we need to first load our decoration plugin // once it's loaded we can provide the Borders and access them from C++ side // so let's try to locate our plugin: QString pluginPath; for (const QString &path : m_engine->importPathList()) { QDirIterator it(path, QDirIterator::Subdirectories); while (it.hasNext()) { it.next(); QFileInfo fileInfo = it.fileInfo(); if (!fileInfo.isFile()) { continue; } if (!fileInfo.path().endsWith(QLatin1String("/org/kde/kwin/decoration"))) { continue; } if (fileInfo.fileName() == QLatin1String("libdecorationplugin.so")) { pluginPath = fileInfo.absoluteFilePath(); break; } } if (!pluginPath.isEmpty()) { break; } } m_engine->importPlugin(pluginPath, "org.kde.kwin.decoration", nullptr); qmlRegisterType("org.kde.kwin.decoration", 0, 1, "Borders"); qmlRegisterType(); qmlRegisterType(); qRegisterMetaType(); } static QString findTheme(const QVariantList &args) { if (args.isEmpty()) { return QString(); } const auto map = args.first().toMap(); auto it = map.constFind(QStringLiteral("theme")); if (it == map.constEnd()) { return QString(); } return it.value().toString(); } Decoration::Decoration(QObject *parent, const QVariantList &args) : KDecoration2::Decoration(parent, args) , m_item(nullptr) , m_borders(nullptr) , m_maximizedBorders(nullptr) , m_extendedBorders(nullptr) , m_padding(nullptr) , m_themeName(s_defaultTheme) , m_view(nullptr) { m_themeName = findTheme(args); Helper::instance().ref(); } Decoration::~Decoration() { delete m_qmlContext; delete m_view; Helper::instance().unref(); } void Decoration::init() { KDecoration2::Decoration::init(); auto s = settings(); connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, this, &Decoration::configChanged); m_qmlContext = new QQmlContext(Helper::instance().rootContext(), this); m_qmlContext->setContextProperty(QStringLiteral("decoration"), this); m_qmlContext->setContextProperty(QStringLiteral("decorationSettings"), s.data()); auto component = Helper::instance().component(m_themeName); if (!component) { return; } if (component == Helper::instance().svgComponent()) { // load SVG theme const QString themeName = m_themeName.mid(16); KConfig config(QLatin1String("aurorae/themes/") + themeName + QLatin1Char('/') + themeName + QLatin1String("rc"), KConfig::FullConfig, QStandardPaths::GenericDataLocation); AuroraeTheme *theme = new AuroraeTheme(this); theme->loadTheme(themeName, config); theme->setBorderSize(s->borderSize()); connect(s.data(), &KDecoration2::DecorationSettings::borderSizeChanged, theme, &AuroraeTheme::setBorderSize); auto readButtonSize = [this, theme] { const KSharedConfigPtr conf = KSharedConfig::openConfig(QStringLiteral("auroraerc")); const KConfigGroup themeGroup(conf, m_themeName.mid(16)); theme->setButtonSize((KDecoration2::BorderSize)(themeGroup.readEntry("ButtonSize", int(KDecoration2::BorderSize::Normal) - s_indexMapper) + s_indexMapper)); }; connect(this, &Decoration::configChanged, theme, readButtonSize); readButtonSize(); // m_theme->setTabDragMimeType(tabDragMimeType()); m_qmlContext->setContextProperty(QStringLiteral("auroraeTheme"), theme); } m_item = qobject_cast< QQuickItem* >(component->create(m_qmlContext)); if (!m_item) { if (component->isError()) { const auto errors = component->errors(); for (const auto &error: errors) { qCWarning(AURORAE) << error; } } return; } m_item->setParent(m_qmlContext); QVariant visualParent = property("visualParent"); if (visualParent.isValid()) { m_item->setParentItem(visualParent.value()); visualParent.value()->setProperty("drawBackground", false); } else { m_view = new KWin::EffectQuickView(this, KWin::EffectQuickView::ExportMode::Image); m_item->setParentItem(m_view->contentItem()); auto updateSize = [this]() { m_item->setSize(m_view->contentItem()->size()); }; updateSize(); connect(m_view->contentItem(), &QQuickItem::widthChanged, m_item, updateSize); connect(m_view->contentItem(), &QQuickItem::heightChanged, m_item, updateSize); connect(m_view, &KWin::EffectQuickView::repaintNeeded, this, &Decoration::updateBuffer); } setupBorders(m_item); // TODO: Is there a more efficient way to react to border changes? auto trackBorders = [this](KWin::Borders *borders) { if (!borders) { return; } connect(borders, &KWin::Borders::leftChanged, this, &Decoration::updateBorders); connect(borders, &KWin::Borders::rightChanged, this, &Decoration::updateBorders); connect(borders, &KWin::Borders::topChanged, this, &Decoration::updateBorders); connect(borders, &KWin::Borders::bottomChanged, this, &Decoration::updateBorders); }; trackBorders(m_borders); trackBorders(m_maximizedBorders); if (m_extendedBorders) { - auto updateExtendedBorders = [this] { - setResizeOnlyBorders(*m_extendedBorders); - }; updateExtendedBorders(); - connect(m_extendedBorders, &KWin::Borders::leftChanged, this, updateExtendedBorders); - connect(m_extendedBorders, &KWin::Borders::rightChanged, this, updateExtendedBorders); - connect(m_extendedBorders, &KWin::Borders::topChanged, this, updateExtendedBorders); - connect(m_extendedBorders, &KWin::Borders::bottomChanged, this, updateExtendedBorders); + connect(m_extendedBorders, &KWin::Borders::leftChanged, this, &Decoration::updateExtendedBorders); + connect(m_extendedBorders, &KWin::Borders::rightChanged, this, &Decoration::updateExtendedBorders); + connect(m_extendedBorders, &KWin::Borders::topChanged, this, &Decoration::updateExtendedBorders); + connect(m_extendedBorders, &KWin::Borders::bottomChanged, this, &Decoration::updateExtendedBorders); } connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateBorders, Qt::QueuedConnection); connect(client().data(), &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::updateBorders); updateBorders(); if (m_view) { auto resizeWindow = [this] { QRect rect(QPoint(0, 0), size()); if (m_padding && !client().data()->isMaximized()) { rect = rect.adjusted(-m_padding->left(), -m_padding->top(), m_padding->right(), m_padding->bottom()); } m_view->setGeometry(rect); }; connect(this, &Decoration::bordersChanged, this, resizeWindow); connect(client().data(), &KDecoration2::DecoratedClient::widthChanged, this, resizeWindow); connect(client().data(), &KDecoration2::DecoratedClient::heightChanged, this, resizeWindow); connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, resizeWindow); connect(client().data(), &KDecoration2::DecoratedClient::shadedChanged, this, resizeWindow); resizeWindow(); updateBuffer(); } else { // create a dummy shadow for the configuration interface if (m_padding) { auto s = QSharedPointer::create(); s->setPadding(*m_padding); s->setInnerShadowRect(QRect(m_padding->left(), m_padding->top(), 1, 1)); setShadow(s); } } } QVariant Decoration::readConfig(const QString &key, const QVariant &defaultValue) { KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("auroraerc")); return config->group(m_themeName).readEntry(key, defaultValue); } void Decoration::setupBorders(QQuickItem *item) { m_borders = item->findChild(QStringLiteral("borders")); m_maximizedBorders = item->findChild(QStringLiteral("maximizedBorders")); m_extendedBorders = item->findChild(QStringLiteral("extendedBorders")); m_padding = item->findChild(QStringLiteral("padding")); } void Decoration::updateBorders() { KWin::Borders *b = m_borders; if (client().data()->isMaximized() && m_maximizedBorders) { b = m_maximizedBorders; } if (!b) { return; } setBorders(*b); + + updateExtendedBorders(); } void Decoration::paint(QPainter *painter, const QRect &repaintRegion) { Q_UNUSED(repaintRegion) if (!m_view) { return; } painter->fillRect(rect(), Qt::transparent); painter->drawImage(rect(), m_view->bufferAsImage(), m_contentRect); } void Decoration::updateShadow() { if (!m_view) { return; } bool updateShadow = false; const auto oldShadow = shadow(); if (m_padding && (m_padding->left() > 0 || m_padding->top() > 0 || m_padding->right() > 0 || m_padding->bottom() > 0) && !client().data()->isMaximized()) { if (oldShadow.isNull()) { updateShadow = true; } else { // compare padding if (oldShadow->padding() != *m_padding) { updateShadow = true; } } const QImage m_buffer = m_view->bufferAsImage(); QImage img(m_buffer.size(), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::transparent); QPainter p(&img); // top p.drawImage(0, 0, m_buffer, 0, 0, img.width(), m_padding->top()); // left p.drawImage(0, m_padding->top(), m_buffer, 0, m_padding->top(), m_padding->left(), m_buffer.height() - m_padding->top()); // bottom p.drawImage(m_padding->left(), m_buffer.height() - m_padding->bottom(), m_buffer, m_padding->left(), m_buffer.height() - m_padding->bottom(), m_buffer.width() - m_padding->left(), m_padding->bottom()); // right p.drawImage(m_buffer.width() - m_padding->right(), m_padding->top(), m_buffer, m_buffer.width() - m_padding->right(), m_padding->top(), m_padding->right(), m_buffer.height() - m_padding->top() - m_padding->bottom()); if (!updateShadow) { updateShadow = (oldShadow->shadow() != img); } if (updateShadow) { auto s = QSharedPointer::create(); s->setShadow(img); s->setPadding(*m_padding); s->setInnerShadowRect(QRect(m_padding->left(), m_padding->top(), m_buffer.width() - m_padding->left() - m_padding->right(), m_buffer.height() - m_padding->top() - m_padding->bottom())); setShadow(s); } } else { if (!oldShadow.isNull()) { setShadow(QSharedPointer()); } } } void Decoration::hoverEnterEvent(QHoverEvent *event) { if (m_view) { event->setAccepted(false); m_view->forwardMouseEvent(event); } KDecoration2::Decoration::hoverEnterEvent(event); } void Decoration::hoverLeaveEvent(QHoverEvent *event) { if (m_view) { m_view->forwardMouseEvent(event); } KDecoration2::Decoration::hoverLeaveEvent(event); } void Decoration::hoverMoveEvent(QHoverEvent *event) { if (m_view) { // turn a hover event into a mouse becase we don't follow hovers as we don't think we have focus QMouseEvent cloneEvent(QEvent::MouseMove, event->posF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier); event->setAccepted(false); m_view->forwardMouseEvent(&cloneEvent); event->setAccepted(cloneEvent.isAccepted()); } KDecoration2::Decoration::hoverMoveEvent(event); } void Decoration::mouseMoveEvent(QMouseEvent *event) { if (m_view) { m_view->forwardMouseEvent(event); } KDecoration2::Decoration::mouseMoveEvent(event); } void Decoration::mousePressEvent(QMouseEvent *event) { if (m_view) { m_view->forwardMouseEvent(event); if (event->button() == Qt::LeftButton) { if (!m_doubleClickTimer.hasExpired(QGuiApplication::styleHints()->mouseDoubleClickInterval())) { QMouseEvent dc(QEvent::MouseButtonDblClick, event->localPos(), event->windowPos(), event->screenPos(), event->button(), event->buttons(), event->modifiers()); m_view->forwardMouseEvent(&dc); } } m_doubleClickTimer.invalidate(); } KDecoration2::Decoration::mousePressEvent(event); } void Decoration::mouseReleaseEvent(QMouseEvent *event) { if (m_view) { m_view->forwardMouseEvent(event); if (event->isAccepted() && event->button() == Qt::LeftButton) { m_doubleClickTimer.start(); } } KDecoration2::Decoration::mouseReleaseEvent(event); } void Decoration::installTitleItem(QQuickItem *item) { auto update = [this, item] { QRect rect = item->mapRectToScene(item->childrenRect()).toRect(); if (rect.isNull()) { rect = item->parentItem()->mapRectToScene(QRectF(item->x(), item->y(), item->width(), item->height())).toRect(); } setTitleBar(rect); }; update(); connect(item, &QQuickItem::widthChanged, this, update); connect(item, &QQuickItem::heightChanged, this, update); connect(item, &QQuickItem::xChanged, this, update); connect(item, &QQuickItem::yChanged, this, update); } +void Decoration::updateExtendedBorders() +{ + // extended sizes + const int extSize = settings()->largeSpacing(); + int extLeft = m_extendedBorders->left(); + int extRight = m_extendedBorders->right(); + int extBottom = m_extendedBorders->bottom(); + + if (settings()->borderSize() == KDecoration2::BorderSize::None) { + if (!client().data()->isMaximizedHorizontally()) { + extLeft = qMax(m_extendedBorders->left(), extSize); + extRight = qMax(m_extendedBorders->right(), extSize); + } + if (!client().data()->isMaximizedVertically()) { + extBottom = qMax(m_extendedBorders->bottom(), extSize); + } + + } else if (settings()->borderSize() == KDecoration2::BorderSize::NoSides && !client().data()->isMaximizedHorizontally() ) { + extLeft = qMax(m_extendedBorders->left(), extSize); + extRight = qMax(m_extendedBorders->right(), extSize); + } + + setResizeOnlyBorders(QMargins(extLeft, 0, extRight, extBottom)); +} + void Decoration::updateBuffer() { m_contentRect = QRect(QPoint(0, 0), m_view->bufferAsImage().size()); if (m_padding && (m_padding->left() > 0 || m_padding->top() > 0 || m_padding->right() > 0 || m_padding->bottom() > 0) && !client().data()->isMaximized()) { m_contentRect = m_contentRect.adjusted(m_padding->left(), m_padding->top(), -m_padding->right(), -m_padding->bottom()); } updateShadow(); update(); } KDecoration2::DecoratedClient *Decoration::clientPointer() const { return client().data(); } ThemeFinder::ThemeFinder(QObject *parent, const QVariantList &args) : QObject(parent) { Q_UNUSED(args) init(); } void ThemeFinder::init() { findAllQmlThemes(); findAllSvgThemes(); } void ThemeFinder::findAllQmlThemes() { const auto offers = KPackage::PackageLoader::self()->findPackages(QStringLiteral("KWin/Decoration"), s_qmlPackageFolder); for (const auto &offer : offers) { m_themes.insert(offer.name(), offer.pluginId()); } } void ThemeFinder::findAllSvgThemes() { QStringList themes; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("aurorae/themes/"), QStandardPaths::LocateDirectory); QStringList themeDirectories; for (const QString &dir : dirs) { QDir directory = QDir(dir); for (const QString &themeDir : directory.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { themeDirectories << dir + themeDir; } } for (const QString &dir : themeDirectories) { for (const QString & file : QDir(dir).entryList(QStringList() << QStringLiteral("metadata.desktop"))) { themes.append(dir + '/' + file); } } for (const QString & theme : themes) { int themeSepIndex = theme.lastIndexOf('/', -1); QString themeRoot = theme.left(themeSepIndex); int themeNameSepIndex = themeRoot.lastIndexOf('/', -1); QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1); KDesktopFile df(theme); QString name = df.readName(); if (name.isEmpty()) { name = packageName; } m_themes.insert(name, QString(QLatin1String("__aurorae__svg__") + packageName)); } } static const QString s_configUiPath = QStringLiteral("kwin/decorations/%1/contents/ui/config.ui"); static const QString s_configXmlPath = QStringLiteral("kwin/decorations/%1/contents/config/main.xml"); bool ThemeFinder::hasConfiguration(const QString &theme) const { if (theme.startsWith(QLatin1String("__aurorae__svg__"))) { return true; } const QString ui = QStandardPaths::locate(QStandardPaths::GenericDataLocation, s_configUiPath.arg(theme)); const QString xml = QStandardPaths::locate(QStandardPaths::GenericDataLocation, s_configXmlPath.arg(theme)); return !(ui.isEmpty() || xml.isEmpty()); } ConfigurationModule::ConfigurationModule(QWidget *parent, const QVariantList &args) : KCModule(parent, args) , m_theme(findTheme(args)) , m_buttonSize(int(KDecoration2::BorderSize::Normal) - s_indexMapper) { setLayout(new QVBoxLayout(this)); init(); } void ConfigurationModule::init() { if (m_theme.startsWith(QLatin1String("__aurorae__svg__"))) { // load the generic setting module initSvg(); } else { initQml(); } } void ConfigurationModule::initSvg() { QWidget *form = new QWidget(this); form->setLayout(new QHBoxLayout(form)); QComboBox *sizes = new QComboBox(form); sizes->addItem(i18nc("@item:inlistbox Button size:", "Tiny")); sizes->addItem(i18nc("@item:inlistbox Button size:", "Normal")); sizes->addItem(i18nc("@item:inlistbox Button size:", "Large")); sizes->addItem(i18nc("@item:inlistbox Button size:", "Very Large")); sizes->addItem(i18nc("@item:inlistbox Button size:", "Huge")); sizes->addItem(i18nc("@item:inlistbox Button size:", "Very Huge")); sizes->addItem(i18nc("@item:inlistbox Button size:", "Oversized")); sizes->setObjectName(QStringLiteral("kcfg_ButtonSize")); QLabel *label = new QLabel(i18n("Button size:"), form); label->setBuddy(sizes); form->layout()->addWidget(label); form->layout()->addWidget(sizes); layout()->addWidget(form); KCoreConfigSkeleton *skel = new KCoreConfigSkeleton(KSharedConfig::openConfig(QStringLiteral("auroraerc")), this); skel->setCurrentGroup(m_theme.mid(16)); skel->addItemInt(QStringLiteral("ButtonSize"), m_buttonSize, int(KDecoration2::BorderSize::Normal) - s_indexMapper, QStringLiteral("ButtonSize")); addConfig(skel, form); } void ConfigurationModule::initQml() { const QString ui = QStandardPaths::locate(QStandardPaths::GenericDataLocation, s_configUiPath.arg(m_theme)); const QString xml = QStandardPaths::locate(QStandardPaths::GenericDataLocation, s_configXmlPath.arg(m_theme)); if (ui.isEmpty() || xml.isEmpty()) { return; } KLocalizedTranslator *translator = new KLocalizedTranslator(this); QCoreApplication::instance()->installTranslator(translator); const KDesktopFile metaData(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/decorations/%1/metadata.desktop").arg(m_theme))); const QString translationDomain = metaData.desktopGroup().readEntry("X-KWin-Config-TranslationDomain", QString()); if (!translationDomain.isEmpty()) { translator->setTranslationDomain(translationDomain); } // load the KConfigSkeleton QFile configFile(xml); KSharedConfigPtr auroraeConfig = KSharedConfig::openConfig("auroraerc"); KConfigGroup configGroup = auroraeConfig->group(m_theme); m_skeleton = new KConfigLoader(configGroup, &configFile, this); // load the ui file QUiLoader *loader = new QUiLoader(this); loader->setLanguageChangeEnabled(true); QFile uiFile(ui); uiFile.open(QFile::ReadOnly); QWidget *customConfigForm = loader->load(&uiFile, this); translator->addContextToMonitor(customConfigForm->objectName()); uiFile.close(); layout()->addWidget(customConfigForm); // connect the ui file with the skeleton addConfig(m_skeleton, customConfigForm); // send a custom event to the translator to retranslate using our translator QEvent le(QEvent::LanguageChange); QCoreApplication::sendEvent(customConfigForm, &le); } } #include "aurorae.moc" diff --git a/plugins/kdecorations/aurorae/src/aurorae.h b/plugins/kdecorations/aurorae/src/aurorae.h index 00a50d3ab..65fb024f7 100644 --- a/plugins/kdecorations/aurorae/src/aurorae.h +++ b/plugins/kdecorations/aurorae/src/aurorae.h @@ -1,128 +1,130 @@ /******************************************************************** Copyright (C) 2009, 2010, 2012 Martin Gräßlin 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, see . *********************************************************************/ #ifndef AURORAE_H #define AURORAE_H #include #include #include #include class QQmlComponent; class QQmlContext; class QQmlEngine; class QQuickItem; class KConfigLoader; namespace KWin { class Borders; class EffectQuickView; } namespace Aurorae { class Decoration : public KDecoration2::Decoration { Q_OBJECT Q_PROPERTY(KDecoration2::DecoratedClient* client READ clientPointer CONSTANT) public: explicit Decoration(QObject *parent = nullptr, const QVariantList &args = QVariantList()); ~Decoration() override; void paint(QPainter *painter, const QRect &repaintRegion) override; Q_INVOKABLE QVariant readConfig(const QString &key, const QVariant &defaultValue = QVariant()); KDecoration2::DecoratedClient *clientPointer() const; public Q_SLOTS: void init() override; void installTitleItem(QQuickItem *item); void updateShadow(); Q_SIGNALS: void configChanged(); protected: void hoverEnterEvent(QHoverEvent *event) override; void hoverLeaveEvent(QHoverEvent *event) override; void hoverMoveEvent(QHoverEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private: void setupBorders(QQuickItem *item); void updateBorders(); void updateBuffer(); + void updateExtendedBorders(); + QRect m_contentRect; //the geometry of the part of the buffer that is not a shadow when buffer was created. QQuickItem *m_item = nullptr; QQmlContext *m_qmlContext = nullptr; KWin::Borders *m_borders; KWin::Borders *m_maximizedBorders; KWin::Borders *m_extendedBorders; KWin::Borders *m_padding; QString m_themeName; KWin::EffectQuickView *m_view; QElapsedTimer m_doubleClickTimer; }; class ThemeFinder : public QObject { Q_OBJECT Q_PROPERTY(QVariantMap themes READ themes) public: explicit ThemeFinder(QObject *parent = nullptr, const QVariantList &args = QVariantList()); QVariantMap themes() const { return m_themes; } public Q_SLOTS: bool hasConfiguration(const QString &theme) const; private: void init(); void findAllQmlThemes(); void findAllSvgThemes(); QVariantMap m_themes; }; class ConfigurationModule : public KCModule { Q_OBJECT public: ConfigurationModule(QWidget *parent, const QVariantList &args); private: void init(); void initSvg(); void initQml(); QString m_theme; KConfigLoader *m_skeleton = nullptr; int m_buttonSize; }; } #endif diff --git a/plugins/kdecorations/aurorae/src/lib/auroraetheme.cpp b/plugins/kdecorations/aurorae/src/lib/auroraetheme.cpp index ba8554fe5..121467d63 100644 --- a/plugins/kdecorations/aurorae/src/lib/auroraetheme.cpp +++ b/plugins/kdecorations/aurorae/src/lib/auroraetheme.cpp @@ -1,505 +1,511 @@ /* Library for Aurorae window decoration themes. Copyright (C) 2009, 2010, 2012 Martin Gräßlin 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 "auroraetheme.h" #include "themeconfig.h" // Qt #include #include #include // KDE #include #include Q_LOGGING_CATEGORY(AURORAE, "aurorae", QtCriticalMsg) namespace Aurorae { /************************************************ * AuroraeThemePrivate ************************************************/ class AuroraeThemePrivate { public: AuroraeThemePrivate(); ~AuroraeThemePrivate(); void initButtonFrame(AuroraeButtonType type); QString themeName; Aurorae::ThemeConfig themeConfig; QHash< AuroraeButtonType, QString > pathes; bool activeCompositing; KDecoration2::BorderSize borderSize; KDecoration2::BorderSize buttonSize; QString dragMimeType; QString decorationPath; }; AuroraeThemePrivate::AuroraeThemePrivate() :activeCompositing(true) , borderSize(KDecoration2::BorderSize::Normal) , buttonSize(KDecoration2::BorderSize::Normal) { } AuroraeThemePrivate::~AuroraeThemePrivate() { } void AuroraeThemePrivate::initButtonFrame(AuroraeButtonType type) { QString file(QLatin1String("aurorae/themes/") + themeName + QLatin1Char('/') + AuroraeTheme::mapButtonToName(type) + QLatin1String(".svg")); QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, file); if (path.isEmpty()) { // let's look for svgz file += QLatin1String("z"); path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, file); } if (!path.isEmpty()) { pathes[ type ] = path; } else { qCDebug(AURORAE) << "No button for: " << AuroraeTheme::mapButtonToName(type); } } /************************************************ * AuroraeTheme ************************************************/ AuroraeTheme::AuroraeTheme(QObject* parent) : QObject(parent) , d(new AuroraeThemePrivate) { connect(this, SIGNAL(themeChanged()), SIGNAL(borderSizesChanged())); connect(this, SIGNAL(buttonSizesChanged()), SIGNAL(borderSizesChanged())); } AuroraeTheme::~AuroraeTheme() { delete d; } bool AuroraeTheme::isValid() const { return !d->themeName.isNull(); } void AuroraeTheme::loadTheme(const QString &name) { KConfig conf(QStringLiteral("auroraerc")); KConfig config(QLatin1String("aurorae/themes/") + name + QLatin1Char('/') + name + QLatin1String("rc"), KConfig::FullConfig, QStandardPaths::GenericDataLocation); KConfigGroup themeGroup(&conf, name); loadTheme(name, config); } void AuroraeTheme::loadTheme(const QString &name, const KConfig &config) { d->themeName = name; QString file(QLatin1String("aurorae/themes/") + d->themeName + QLatin1String("/decoration.svg")); QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, file); if (path.isEmpty()) { file += QLatin1String("z"); path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, file); } if (path.isEmpty()) { qCDebug(AURORAE) << "Could not find decoration svg: aborting"; d->themeName.clear(); return; } d->decorationPath = path; // load the buttons d->initButtonFrame(MinimizeButton); d->initButtonFrame(MaximizeButton); d->initButtonFrame(RestoreButton); d->initButtonFrame(CloseButton); d->initButtonFrame(AllDesktopsButton); d->initButtonFrame(KeepAboveButton); d->initButtonFrame(KeepBelowButton); d->initButtonFrame(ShadeButton); d->initButtonFrame(HelpButton); d->themeConfig.load(config); emit themeChanged(); } bool AuroraeTheme::hasButton(AuroraeButtonType button) const { return d->pathes.contains(button); } QLatin1String AuroraeTheme::mapButtonToName(AuroraeButtonType type) { switch(type) { case MinimizeButton: return QLatin1String("minimize"); case MaximizeButton: return QLatin1String("maximize"); case RestoreButton: return QLatin1String("restore"); case CloseButton: return QLatin1String("close"); case AllDesktopsButton: return QLatin1String("alldesktops"); case KeepAboveButton: return QLatin1String("keepabove"); case KeepBelowButton: return QLatin1String("keepbelow"); case ShadeButton: return QLatin1String("shade"); case HelpButton: return QLatin1String("help"); case MenuButton: return QLatin1String("menu"); case AppMenuButton: return QLatin1String("appmenu"); default: return QLatin1String(""); } } const QString &AuroraeTheme::themeName() const { return d->themeName; } void AuroraeTheme::borders(int& left, int& top, int& right, int& bottom, bool maximized) const { const qreal titleHeight = qMax((qreal)d->themeConfig.titleHeight(), d->themeConfig.buttonHeight()*buttonSizeFactor() + d->themeConfig.buttonMarginTop()); if (maximized) { const qreal title = titleHeight + d->themeConfig.titleEdgeTopMaximized() + d->themeConfig.titleEdgeBottomMaximized(); switch ((DecorationPosition)d->themeConfig.decorationPosition()) { case DecorationTop: left = right = bottom = 0; top = title; break; case DecorationBottom: left = right = top = 0; bottom = title; break; case DecorationLeft: top = right = bottom = 0; left = title; break; case DecorationRight: left = top = bottom = 0; right = title; break; default: left = right = bottom = top = 0; break; } } else { + int minMargin; + int maxMargin; switch (d->borderSize) { + case KDecoration2::BorderSize::NoSides: case KDecoration2::BorderSize::Tiny: - // TODO: this looks wrong - if (isCompositingActive()) { - left = qMin(0, (int)left - d->themeConfig.borderLeft() - d->themeConfig.paddingLeft()); - right = qMin(0, (int)right - d->themeConfig.borderRight() - d->themeConfig.paddingRight()); - bottom = qMin(0, (int)bottom - d->themeConfig.borderBottom() - d->themeConfig.paddingBottom()); - } else { - left = qMin(0, (int)left - d->themeConfig.borderLeft()); - right = qMin(0, (int)right - d->themeConfig.borderRight()); - bottom = qMin(0, (int)bottom - d->themeConfig.borderBottom()); - } + minMargin = 1; + maxMargin = 4; + break; + case KDecoration2::BorderSize::Normal: + minMargin = 4; + maxMargin = 6; break; case KDecoration2::BorderSize::Large: - left = right = bottom = top = 4; + minMargin = 6; + maxMargin = 8; break; case KDecoration2::BorderSize::VeryLarge: - left = right = bottom = top = 8; + minMargin = 8; + maxMargin = 12; break; case KDecoration2::BorderSize::Huge: - left = right = bottom = top = 12; + minMargin = 12; + maxMargin = 20; break; case KDecoration2::BorderSize::VeryHuge: - left = right = bottom = top = 23; + minMargin = 23; + maxMargin = 30; break; case KDecoration2::BorderSize::Oversized: - left = right = bottom = top = 36; + minMargin = 36; + maxMargin = 48; break; - case KDecoration2::BorderSize::Normal: default: - left = right = bottom = top = 0; + minMargin = 0; + maxMargin = 0; + } + + left = qBound(minMargin, d->themeConfig.borderLeft(), maxMargin); + right = qBound(minMargin, d->themeConfig.borderRight(), maxMargin); + bottom = qBound(minMargin, d->themeConfig.borderBottom(), maxMargin); + + if (d->borderSize == KDecoration2::BorderSize::None) { + left = 0; + right = 0; + bottom = 0; + } else if (d->borderSize == KDecoration2::BorderSize::NoSides) { + left = 0; + right = 0; } + const qreal title = titleHeight + d->themeConfig.titleEdgeTop() + d->themeConfig.titleEdgeBottom(); switch ((DecorationPosition)d->themeConfig.decorationPosition()) { case DecorationTop: - left += d->themeConfig.borderLeft(); - right += d->themeConfig.borderRight(); - bottom += d->themeConfig.borderBottom(); top = title; break; case DecorationBottom: - left += d->themeConfig.borderLeft(); - right += d->themeConfig.borderRight(); bottom = title; - top += d->themeConfig.borderTop(); break; case DecorationLeft: left = title; - right += d->themeConfig.borderRight(); - bottom += d->themeConfig.borderBottom(); - top += d->themeConfig.borderTop(); break; case DecorationRight: - left += d->themeConfig.borderLeft(); right = title; - bottom += d->themeConfig.borderBottom(); - top += d->themeConfig.borderTop(); break; default: left = right = bottom = top = 0; break; } } } int AuroraeTheme::bottomBorder() const { int left, top, right, bottom; left = top = right = bottom = 0; borders(left, top, right, bottom, false); return bottom; } int AuroraeTheme::leftBorder() const { int left, top, right, bottom; left = top = right = bottom = 0; borders(left, top, right, bottom, false); return left; } int AuroraeTheme::rightBorder() const { int left, top, right, bottom; left = top = right = bottom = 0; borders(left, top, right, bottom, false); return right; } int AuroraeTheme::topBorder() const { int left, top, right, bottom; left = top = right = bottom = 0; borders(left, top, right, bottom, false); return top; } int AuroraeTheme::bottomBorderMaximized() const { int left, top, right, bottom; left = top = right = bottom = 0; borders(left, top, right, bottom, true); return bottom; } int AuroraeTheme::leftBorderMaximized() const { int left, top, right, bottom; left = top = right = bottom = 0; borders(left, top, right, bottom, true); return left; } int AuroraeTheme::rightBorderMaximized() const { int left, top, right, bottom; left = top = right = bottom = 0; borders(left, top, right, bottom, true); return right; } int AuroraeTheme::topBorderMaximized() const { int left, top, right, bottom; left = top = right = bottom = 0; borders(left, top, right, bottom, true); return top; } void AuroraeTheme::padding(int& left, int& top, int& right, int& bottom) const { left = d->themeConfig.paddingLeft(); top = d->themeConfig.paddingTop(); right = d->themeConfig.paddingRight(); bottom = d->themeConfig.paddingBottom(); } #define THEME_CONFIG( prototype ) \ int AuroraeTheme::prototype ( ) const \ { \ return d->themeConfig.prototype( ); \ } THEME_CONFIG(paddingBottom) THEME_CONFIG(paddingLeft) THEME_CONFIG(paddingRight) THEME_CONFIG(paddingTop) THEME_CONFIG(buttonWidth) THEME_CONFIG(buttonWidthMinimize) THEME_CONFIG(buttonWidthMaximizeRestore) THEME_CONFIG(buttonWidthClose) THEME_CONFIG(buttonWidthAllDesktops) THEME_CONFIG(buttonWidthKeepAbove) THEME_CONFIG(buttonWidthKeepBelow) THEME_CONFIG(buttonWidthShade) THEME_CONFIG(buttonWidthHelp) THEME_CONFIG(buttonWidthMenu) THEME_CONFIG(buttonWidthAppMenu) THEME_CONFIG(buttonHeight) THEME_CONFIG(buttonSpacing) THEME_CONFIG(buttonMarginTop) THEME_CONFIG(explicitButtonSpacer) THEME_CONFIG(animationTime) THEME_CONFIG(titleEdgeLeft) THEME_CONFIG(titleEdgeRight) THEME_CONFIG(titleEdgeTop) THEME_CONFIG(titleEdgeLeftMaximized) THEME_CONFIG(titleEdgeRightMaximized) THEME_CONFIG(titleEdgeTopMaximized) THEME_CONFIG(titleBorderLeft) THEME_CONFIG(titleBorderRight) THEME_CONFIG(titleHeight) #undef THEME_CONFIG #define THEME_CONFIG_TYPE( rettype, prototype ) \ rettype AuroraeTheme::prototype ( ) const \ {\ return d->themeConfig.prototype(); \ } THEME_CONFIG_TYPE(QColor, activeTextColor) THEME_CONFIG_TYPE(QColor, inactiveTextColor) THEME_CONFIG_TYPE(Qt::Alignment, alignment) THEME_CONFIG_TYPE(Qt::Alignment, verticalAlignment) #undef THEME_CONFIG_TYPE QString AuroraeTheme::decorationPath() const { return d->decorationPath; } #define BUTTON_PATH( prototype, buttonType ) \ QString AuroraeTheme::prototype ( ) const \ { \ if (hasButton( buttonType )) { \ return d->pathes[ buttonType ]; \ } else { \ return QString(); \ } \ }\ BUTTON_PATH(minimizeButtonPath, MinimizeButton) BUTTON_PATH(maximizeButtonPath, MaximizeButton) BUTTON_PATH(restoreButtonPath, RestoreButton) BUTTON_PATH(closeButtonPath, CloseButton) BUTTON_PATH(allDesktopsButtonPath, AllDesktopsButton) BUTTON_PATH(keepAboveButtonPath, KeepAboveButton) BUTTON_PATH(keepBelowButtonPath, KeepBelowButton) BUTTON_PATH(shadeButtonPath, ShadeButton) BUTTON_PATH(helpButtonPath, HelpButton) #undef BUTTON_PATH void AuroraeTheme::titleEdges(int &left, int &top, int &right, int &bottom, bool maximized) const { if (maximized) { left = d->themeConfig.titleEdgeLeftMaximized(); top = d->themeConfig.titleEdgeTopMaximized(); right = d->themeConfig.titleEdgeRightMaximized(); bottom = d->themeConfig.titleEdgeBottomMaximized(); } else { left = d->themeConfig.titleEdgeLeft(); top = d->themeConfig.titleEdgeTop(); right = d->themeConfig.titleEdgeRight(); bottom = d->themeConfig.titleEdgeBottom(); } } bool AuroraeTheme::isCompositingActive() const { return d->activeCompositing; } void AuroraeTheme::setCompositingActive(bool active) { d->activeCompositing = active; } void AuroraeTheme::setBorderSize(KDecoration2::BorderSize size) { if (d->borderSize == size) { return; } d->borderSize = size; emit borderSizesChanged(); } void AuroraeTheme::setButtonSize(KDecoration2::BorderSize size) { if (d->buttonSize == size) { return; } d->buttonSize = size; emit buttonSizesChanged(); } void AuroraeTheme::setTabDragMimeType(const QString &mime) { d->dragMimeType = mime; } const QString &AuroraeTheme::tabDragMimeType() const { return d->dragMimeType; } qreal AuroraeTheme::buttonSizeFactor() const { switch (d->buttonSize) { case KDecoration2::BorderSize::Tiny: return 0.8; case KDecoration2::BorderSize::Large: return 1.2; case KDecoration2::BorderSize::VeryLarge: return 1.4; case KDecoration2::BorderSize::Huge: return 1.6; case KDecoration2::BorderSize::VeryHuge: return 1.8; case KDecoration2::BorderSize::Oversized: return 2.0; case KDecoration2::BorderSize::Normal: // fall through default: return 1.0; } } DecorationPosition AuroraeTheme::decorationPosition() const { return (DecorationPosition)d->themeConfig.decorationPosition(); } } // namespace diff --git a/plugins/kdecorations/aurorae/src/qml/aurorae.qml b/plugins/kdecorations/aurorae/src/qml/aurorae.qml index 5b8124113..95339457a 100644 --- a/plugins/kdecorations/aurorae/src/qml/aurorae.qml +++ b/plugins/kdecorations/aurorae/src/qml/aurorae.qml @@ -1,220 +1,231 @@ /******************************************************************** Copyright (C) 2012 Martin Gräßlin 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, see . *********************************************************************/ import QtQuick 2.0 import org.kde.kwin.decoration 0.1 import org.kde.plasma.core 2.0 as PlasmaCore Decoration { id: root property bool animate: false Component.onCompleted: { borders.left = Qt.binding(function() { return Math.max(0, auroraeTheme.borderLeft);}); borders.right = Qt.binding(function() { return Math.max(0, auroraeTheme.borderRight);}); borders.top = Qt.binding(function() { return Math.max(0, auroraeTheme.borderTop);}); borders.bottom = Qt.binding(function() { return Math.max(0, auroraeTheme.borderBottom);}); maximizedBorders.left = Qt.binding(function() { return Math.max(0, auroraeTheme.borderLeftMaximized);}); maximizedBorders.right = Qt.binding(function() { return Math.max(0, auroraeTheme.borderRightMaximized);}); maximizedBorders.bottom = Qt.binding(function() { return Math.max(0, auroraeTheme.borderBottomMaximized);}); maximizedBorders.top = Qt.binding(function() { return Math.max(0, auroraeTheme.borderTopMaximized);}); padding.left = auroraeTheme.paddingLeft; padding.right = auroraeTheme.paddingRight; padding.bottom = auroraeTheme.paddingBottom; padding.top = auroraeTheme.paddingTop; root.animate = true; } DecorationOptions { id: options deco: decoration } Item { id: titleRect x: decoration.client.maximized ? maximizedBorders.left : borders.left y: decoration.client.maximized ? 0 : root.borders.bottom width: decoration.client.width//parent.width - x - (decoration.client.maximized ? maximizedBorders.right : borders.right) height: decoration.client.maximized ? maximizedBorders.top : borders.top Component.onCompleted: { decoration.installTitleItem(titleRect); } } PlasmaCore.FrameSvg { property bool supportsInactive: hasElementPrefix("decoration-inactive") property bool supportsMaximized: hasElementPrefix("decoration-maximized") property bool supportsMaximizedInactive: hasElementPrefix("decoration-maximized-inactive") property bool supportsInnerBorder: hasElementPrefix("innerborder") property bool supportsInnerBorderInactive: hasElementPrefix("innerborder-inactive") id: backgroundSvg imagePath: auroraeTheme.decorationPath } PlasmaCore.FrameSvgItem { id: decorationActive property bool shown: (!decoration.client.maximized || !backgroundSvg.supportsMaximized) && (decoration.client.active || !backgroundSvg.supportsInactive) anchors.fill: parent imagePath: backgroundSvg.imagePath prefix: "decoration" opacity: shown ? 1 : 0 enabledBorders: decoration.client.maximized ? PlasmaCore.FrameSvg.NoBorder : PlasmaCore.FrameSvg.TopBorder | PlasmaCore.FrameSvg.BottomBorder | PlasmaCore.FrameSvg.LeftBorder | PlasmaCore.FrameSvg.RightBorder Behavior on opacity { enabled: root.animate NumberAnimation { duration: auroraeTheme.animationTime } } } PlasmaCore.FrameSvgItem { id: decorationInactive anchors.fill: parent imagePath: backgroundSvg.imagePath prefix: "decoration-inactive" opacity: (!decoration.client.active && backgroundSvg.supportsInactive) ? 1 : 0 enabledBorders: decoration.client.maximized ? PlasmaCore.FrameSvg.NoBorder : PlasmaCore.FrameSvg.TopBorder | PlasmaCore.FrameSvg.BottomBorder | PlasmaCore.FrameSvg.LeftBorder | PlasmaCore.FrameSvg.RightBorder Behavior on opacity { enabled: root.animate NumberAnimation { duration: auroraeTheme.animationTime } } } PlasmaCore.FrameSvgItem { id: decorationMaximized property bool shown: decoration.client.maximized && backgroundSvg.supportsMaximized && (decoration.client.active || !backgroundSvg.supportsMaximizedInactive) anchors { left: parent.left right: parent.right top: parent.top leftMargin: 0 rightMargin: 0 topMargin: 0 } imagePath: backgroundSvg.imagePath prefix: "decoration-maximized" height: parent.maximizedBorders.top opacity: shown ? 1 : 0 enabledBorders: PlasmaCore.FrameSvg.NoBorder Behavior on opacity { enabled: root.animate NumberAnimation { duration: auroraeTheme.animationTime } } } PlasmaCore.FrameSvgItem { id: decorationMaximizedInactive anchors { left: parent.left right: parent.right top: parent.top leftMargin: 0 rightMargin: 0 topMargin: 0 } imagePath: backgroundSvg.imagePath prefix: "decoration-maximized-inactive" height: parent.maximizedBorders.top opacity: (!decoration.client.active && decoration.client.maximized && backgroundSvg.supportsMaximizedInactive) ? 1 : 0 enabledBorders: PlasmaCore.FrameSvg.NoBorder Behavior on opacity { enabled: root.animate NumberAnimation { duration: auroraeTheme.animationTime } } } AuroraeButtonGroup { id: leftButtonGroup buttons: options.titleButtonsLeft width: childrenRect.width animate: root.animate anchors { left: root.left leftMargin: decoration.client.maximized ? auroraeTheme.titleEdgeLeftMaximized : (auroraeTheme.titleEdgeLeft + root.padding.left) } } AuroraeButtonGroup { id: rightButtonGroup buttons: options.titleButtonsRight width: childrenRect.width animate: root.animate anchors { right: root.right rightMargin: decoration.client.maximized ? auroraeTheme.titleEdgeRightMaximized : (auroraeTheme.titleEdgeRight + root.padding.right) } } Text { id: caption text: decoration.client.caption textFormat: Text.PlainText horizontalAlignment: auroraeTheme.horizontalAlignment verticalAlignment: auroraeTheme.verticalAlignment elide: Text.ElideRight height: Math.max(auroraeTheme.titleHeight, auroraeTheme.buttonHeight * auroraeTheme.buttonSizeFactor) color: decoration.client.active ? auroraeTheme.activeTextColor : auroraeTheme.inactiveTextColor font: options.titleFont renderType: Text.NativeRendering anchors { left: leftButtonGroup.right right: rightButtonGroup.left top: root.top topMargin: decoration.client.maximized ? auroraeTheme.titleEdgeTopMaximized : (auroraeTheme.titleEdgeTop + root.padding.top) leftMargin: auroraeTheme.titleBorderLeft rightMargin: auroraeTheme.titleBorderRight } Behavior on color { enabled: root.animate ColorAnimation { duration: auroraeTheme.animationTime } } } PlasmaCore.FrameSvgItem { id: innerBorder anchors { fill: parent leftMargin: parent.padding.left + parent.borders.left - margins.left rightMargin: parent.padding.right + parent.borders.right - margins.right topMargin: parent.padding.top + parent.borders.top - margins.top bottomMargin: parent.padding.bottom + parent.borders.bottom - margins.bottom } + visible: parent.borders.left > fixedMargins.left + && parent.borders.right > fixedMargins.right + && parent.borders.top > fixedMargins.top + && parent.borders.bottom > fixedMargins.bottom + imagePath: backgroundSvg.imagePath prefix: "innerborder" opacity: (decoration.client.active && !decoration.client.maximized && backgroundSvg.supportsInnerBorder) ? 1 : 0 Behavior on opacity { enabled: root.animate NumberAnimation { duration: auroraeTheme.animationTime } } } PlasmaCore.FrameSvgItem { id: innerBorderInactive anchors { fill: parent leftMargin: parent.padding.left + parent.borders.left - margins.left rightMargin: parent.padding.right + parent.borders.right - margins.right topMargin: parent.padding.top + parent.borders.top - margins.top bottomMargin: parent.padding.bottom + parent.borders.bottom - margins.bottom } + + visible: parent.borders.left > fixedMargins.left + && parent.borders.right > fixedMargins.right + && parent.borders.top > fixedMargins.top + && parent.borders.bottom > fixedMargins.bottom + imagePath: backgroundSvg.imagePath prefix: "innerborder-inactive" opacity: (!decoration.client.active && !decoration.client.maximized && backgroundSvg.supportsInnerBorderInactive) ? 1 : 0 Behavior on opacity { enabled: root.animate NumberAnimation { duration: auroraeTheme.animationTime } } } }