diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4c232cc --- /dev/null +++ b/.clang-format @@ -0,0 +1,115 @@ +--- +Language: Cpp +# BasedOnStyle: WebKit +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: false +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: All +BreakBeforeBraces: WebKit +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: Inner +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +RawStringFormats: + - Delimiter: pb + Language: TextProto + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7ec7e49 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "array": "cpp", + "string_view": "cpp" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e6f3487 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required (VERSION 3.0) +project (material-decoration) + +add_definitions (-Wall -Werror) + +include (FeatureSummary) +find_package (ECM 0.0.9 REQUIRED NO_MODULE) + +set (CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + ${ECM_MODULE_PATH} + ${ECM_KDE_MODULE_DIR} + "${CMAKE_SOURCE_DIR}/cmake" + "${CMAKE_SOURCE_DIR}/cmake/Modules" +) + +include (ECMInstallIcons) +include (KDEInstallDirs) +include (KDECMakeSettings) +include (KDECompilerSettings NO_POLICY_SCOPE) + +add_subdirectory (src) + +feature_summary(WHAT ALL) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..01b14d5 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,42 @@ +find_package (KDecoration2 REQUIRED) + +find_package (Qt5 REQUIRED COMPONENTS + Core + Gui +) + +find_package (KF5 REQUIRED COMPONENTS + Config + CoreAddons + GuiAddons + WindowSystem +) + +set (decoration_SRCS + CloseButton.cc + Decoration.cc + MaximizeButton.cc + MinimizeButton.cc + plugin.cc +) + +add_library (materialdecoration MODULE + ${decoration_SRCS} +) + +target_link_libraries (materialdecoration + PUBLIC + Qt5::Core + Qt5::Gui + KF5::ConfigCore + KF5::ConfigGui + KF5::CoreAddons + KF5::GuiAddons + KF5::WindowSystem + + PRIVATE + KDecoration2::KDecoration +) + +install (TARGETS materialdecoration + DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kdecoration2) diff --git a/src/CloseButton.cc b/src/CloseButton.cc new file mode 100644 index 0000000..354d523 --- /dev/null +++ b/src/CloseButton.cc @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +// own +#include "CloseButton.h" +#include "Decoration.h" + +// KDecoration +#include + +// Qt +#include + +namespace Material +{ + +CloseButton::CloseButton(Decoration *decoration, QObject *parent) + : DecorationButton(KDecoration2::DecorationButtonType::Close, decoration, parent) +{ + auto *decoratedClient = decoration->client().data(); + connect(decoratedClient, &KDecoration2::DecoratedClient::closeableChanged, + this, &CloseButton::setVisible); + + connect(this, &CloseButton::hoveredChanged, this, + [this] { + update(); + }); + + const int titleBarHeight = decoration->titleBarHeight(); + const QSize size(qRound(titleBarHeight * 1.33), titleBarHeight); + setGeometry(QRect(QPoint(0, 0), size)); + setVisible(decoratedClient->isCloseable()); +} + +CloseButton::~CloseButton() +{ +} + +void CloseButton::paint(QPainter *painter, const QRect &repaintRegion) +{ + Q_UNUSED(repaintRegion) + + const QRectF buttonRect = geometry(); + QRectF crossRect = QRectF(0, 0, 10, 10); + crossRect.moveCenter(buttonRect.center().toPoint()); + + painter->save(); + + painter->setRenderHints(QPainter::Antialiasing, false); + + // Background. + painter->setPen(Qt::NoPen); + painter->setBrush(backgroundColor()); + painter->drawRect(buttonRect); + + // Foreground. + painter->setPen(foregroundColor()); + painter->setBrush(Qt::NoBrush); + painter->drawLine(crossRect.topLeft(), crossRect.bottomRight()); + painter->drawLine(crossRect.topRight(), crossRect.bottomLeft()); + + painter->restore(); +} + +QColor CloseButton::backgroundColor() const +{ + const auto *deco = qobject_cast(decoration()); + if (!deco) { + return {}; + } + + if (isPressed()) { + auto *decoratedClient = deco->client().data(); + return decoratedClient->color( + KDecoration2::ColorGroup::Warning, + KDecoration2::ColorRole::Foreground + ).lighter(); + } + + if (isHovered()) { + auto *decoratedClient = deco->client().data(); + return decoratedClient->color( + KDecoration2::ColorGroup::Warning, + KDecoration2::ColorRole::Foreground + ); + } + + return deco->titleBarBackgroundColor(); +} + +QColor CloseButton::foregroundColor() const +{ + const auto *deco = qobject_cast(decoration()); + if (!deco) { + return {}; + } + + return deco->titleBarForegroundColor(); +} + +} // namespace Material diff --git a/src/CloseButton.h b/src/CloseButton.h new file mode 100644 index 0000000..c30f37d --- /dev/null +++ b/src/CloseButton.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +#pragma once + +// KDecoration +#include + +namespace Material +{ + +class Decoration; + +class CloseButton : public KDecoration2::DecorationButton +{ + Q_OBJECT + +public: + CloseButton(Decoration *decoration, QObject *parent = nullptr); + ~CloseButton() override; + + void paint(QPainter *painter, const QRect &repaintRegion) override; + +private: + QColor backgroundColor() const; + QColor foregroundColor() const; +}; + +} // namespace Material diff --git a/src/Decoration.cc b/src/Decoration.cc new file mode 100644 index 0000000..0b443cd --- /dev/null +++ b/src/Decoration.cc @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +// own +#include "Decoration.h" +#include "CloseButton.h" +#include "MaximizeButton.h" +#include "MinimizeButton.h" +#include "metrics.h" + +// KDecoration +#include +#include + +// Qt +#include + +namespace Material +{ + +Decoration::Decoration(QObject *parent, const QVariantList &args) + : KDecoration2::Decoration(parent, args) +{ +} + +Decoration::~Decoration() +{ +} + +void Decoration::paint(QPainter *painter, const QRect &repaintRegion) +{ + auto *decoratedClient = client().data(); + + if (!decoratedClient->isShaded()) { + paintFrameBackground(painter, repaintRegion); + } + + paintTitleBarBackground(painter, repaintRegion); + paintCaption(painter, repaintRegion); + paintButtons(painter, repaintRegion); +} + +void Decoration::init() +{ + auto *decoratedClient = client().data(); + + connect(decoratedClient, &KDecoration2::DecoratedClient::widthChanged, + this, &Decoration::updateTitleBar); + connect(decoratedClient, &KDecoration2::DecoratedClient::widthChanged, + this, &Decoration::updateButtonsGeometry); + connect(decoratedClient, &KDecoration2::DecoratedClient::maximizedChanged, + this, &Decoration::updateButtonsGeometry); + + auto repaintTitleBar = [this] { + update(titleBar()); + }; + + connect(decoratedClient, &KDecoration2::DecoratedClient::captionChanged, + this, repaintTitleBar); + connect(decoratedClient, &KDecoration2::DecoratedClient::activeChanged, + this, repaintTitleBar); + + updateBorders(); + updateResizeBorders(); + updateTitleBar(); + + auto buttonCreator = [this] (KDecoration2::DecorationButtonType type, KDecoration2::Decoration *decoration, QObject *parent) + -> KDecoration2::DecorationButton* { + Q_UNUSED(decoration) + + switch (type) { + case KDecoration2::DecorationButtonType::Close: + return new CloseButton(this, parent); + + case KDecoration2::DecorationButtonType::Maximize: + return new MaximizeButton(this, parent); + + case KDecoration2::DecorationButtonType::Minimize: + return new MinimizeButton(this, parent); + + default: + return nullptr; + } + }; + + m_leftButtons = new KDecoration2::DecorationButtonGroup( + KDecoration2::DecorationButtonGroup::Position::Left, + this, + buttonCreator); + + m_rightButtons = new KDecoration2::DecorationButtonGroup( + KDecoration2::DecorationButtonGroup::Position::Right, + this, + buttonCreator); + + updateButtonsGeometry(); +} + +void Decoration::updateBorders() +{ + QMargins borders; + borders.setTop(titleBarHeight()); + setBorders(borders); +} + +void Decoration::updateResizeBorders() +{ + QMargins borders; + + const int extender = settings()->largeSpacing(); + borders.setLeft(extender); + borders.setTop(extender); + borders.setRight(extender); + borders.setBottom(extender); + + setResizeOnlyBorders(borders); +} + +void Decoration::updateTitleBar() +{ + auto *decoratedClient = client().data(); + setTitleBar(QRect(0, 0, decoratedClient->width(), titleBarHeight())); +} + +void Decoration::updateButtonsGeometry() +{ + if (!m_leftButtons->buttons().isEmpty()) { + m_leftButtons->setPos(QPointF(0, 0)); + m_leftButtons->setSpacing(0); + } + + if (!m_rightButtons->buttons().isEmpty()) { + m_rightButtons->setPos(QPointF(size().width() - m_rightButtons->geometry().width(), 0)); + m_rightButtons->setSpacing(0); + } + + update(); +} + +int Decoration::titleBarHeight() const +{ + const QFontMetrics fontMetrics(settings()->font()); + const int baseUnit = settings()->gridUnit(); + + int height = 0; + + height += baseUnit * metrics::TitleBar_TopMargin; + height += fontMetrics.height(); + height += baseUnit * metrics::TitleBar_BottomMargin; + + return height; +} + +void Decoration::paintFrameBackground(QPainter *painter, const QRect &repaintRegion) const +{ + Q_UNUSED(repaintRegion) + + const auto *decoratedClient = client().data(); + + painter->save(); + + painter->fillRect(rect(), Qt::transparent); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(decoratedClient->color( + decoratedClient->isActive() + ? KDecoration2::ColorGroup::Active + : KDecoration2::ColorGroup::Inactive, + KDecoration2::ColorRole::Frame)); + painter->setClipRect(0, borderTop(), size().width(), size().height() - borderTop(), Qt::IntersectClip); + painter->drawRect(rect()); + + painter->restore(); +} + +QColor Decoration::titleBarBackgroundColor() const +{ + const auto *decoratedClient = client().data(); + const auto group = decoratedClient->isActive() + ? KDecoration2::ColorGroup::Active + : KDecoration2::ColorGroup::Inactive; + return decoratedClient->color(group, KDecoration2::ColorRole::TitleBar); +} + +QColor Decoration::titleBarForegroundColor() const +{ + const auto *decoratedClient = client().data(); + const auto group = decoratedClient->isActive() + ? KDecoration2::ColorGroup::Active + : KDecoration2::ColorGroup::Inactive; + return decoratedClient->color(group, KDecoration2::ColorRole::Foreground); +} + +void Decoration::paintTitleBarBackground(QPainter *painter, const QRect &repaintRegion) const +{ + Q_UNUSED(repaintRegion) + + const auto *decoratedClient = client().data(); + + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(titleBarBackgroundColor()); + painter->drawRect(QRect(0, 0, decoratedClient->width(), titleBarHeight())); + painter->restore(); +} + +void Decoration::paintCaption(QPainter *painter, const QRect &repaintRegion) const +{ + Q_UNUSED(repaintRegion) + + const auto *decoratedClient = client().data(); + + const int textWidth = settings()->fontMetrics().boundingRect(decoratedClient->caption()).width(); + const QRect textRect((size().width() - textWidth) / 2, 0, textWidth, titleBarHeight()); + + const QRect titleBarRect(0, 0, size().width(), titleBarHeight()); + + const QRect availableRect = titleBarRect.adjusted( + m_leftButtons->geometry().width() + settings()->smallSpacing(), 0, + -(m_rightButtons->geometry().width() + settings()->smallSpacing()), 0 + ); + + QRect captionRect; + Qt::Alignment alignment; + + if (textRect.left() < availableRect.left()) { + captionRect = availableRect; + alignment = Qt::AlignLeft | Qt::AlignVCenter; + } else if (availableRect.right() < textRect.right()) { + captionRect = availableRect; + alignment = Qt::AlignRight | Qt::AlignVCenter; + } else { + captionRect = titleBarRect; + alignment = Qt::AlignCenter; + } + + const QString caption = painter->fontMetrics().elidedText( + decoratedClient->caption(), Qt::ElideMiddle, captionRect.width()); + + painter->save(); + painter->setFont(settings()->font()); + painter->setPen(titleBarForegroundColor()); + painter->drawText(captionRect, alignment, caption); + painter->restore(); +} + +void Decoration::paintButtons(QPainter *painter, const QRect &repaintRegion) const +{ + m_leftButtons->paint(painter, repaintRegion); + m_rightButtons->paint(painter, repaintRegion); +} + +} // namespace Material diff --git a/src/Decoration.h b/src/Decoration.h new file mode 100644 index 0000000..e8cf7bf --- /dev/null +++ b/src/Decoration.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +#pragma once + +// KDecoration +#include +#include + +// Qt +#include + +namespace Material +{ + +class CloseButton; +class MaximizeButton; +class MinimizeButton; + +class Decoration : public KDecoration2::Decoration +{ + Q_OBJECT + +public: + Decoration(QObject *parent = nullptr, const QVariantList &args = QVariantList()); + ~Decoration() override; + + void paint(QPainter *painter, const QRect &repaintRegion) override; + +public slots: + void init() override; + +private: + void updateBorders(); + void updateResizeBorders(); + void updateTitleBar(); + void updateButtonsGeometry(); + + int titleBarHeight() const; + + QColor titleBarBackgroundColor() const; + QColor titleBarForegroundColor() const; + + void paintFrameBackground(QPainter *painter, const QRect &repaintRegion) const; + void paintTitleBarBackground(QPainter *painter, const QRect &repaintRegion) const; + void paintCaption(QPainter *painter, const QRect &repaintRegion) const; + void paintButtons(QPainter *painter, const QRect &repaintRegion) const; + + KDecoration2::DecorationButtonGroup *m_leftButtons; + KDecoration2::DecorationButtonGroup *m_rightButtons; + + friend class CloseButton; + friend class MaximizeButton; + friend class MinimizeButton; +}; + +} // namespace Material diff --git a/src/MaximizeButton.cc b/src/MaximizeButton.cc new file mode 100644 index 0000000..6284f93 --- /dev/null +++ b/src/MaximizeButton.cc @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +// own +#include "MaximizeButton.h" +#include "Decoration.h" + +// KDecoration +#include + +// KF +#include + +// Qt +#include + +namespace Material +{ + +MaximizeButton::MaximizeButton(Decoration *decoration, QObject *parent) + : DecorationButton(KDecoration2::DecorationButtonType::Maximize, decoration, parent) +{ + auto *decoratedClient = decoration->client().data(); + connect(decoratedClient, &KDecoration2::DecoratedClient::maximizeableChanged, + this, &MaximizeButton::setVisible); + + connect(this, &MaximizeButton::hoveredChanged, this, + [this] { + update(); + }); + + const int titleBarHeight = decoration->titleBarHeight(); + const QSize size(qRound(titleBarHeight * 1.33), titleBarHeight); + setGeometry(QRect(QPoint(0, 0), size)); + setVisible(decoratedClient->isMaximizeable()); +} + +MaximizeButton::~MaximizeButton() +{ +} + +void MaximizeButton::paint(QPainter *painter, const QRect &repaintRegion) +{ + Q_UNUSED(repaintRegion) + + const QRectF buttonRect = geometry(); + QRectF maximizeRect = QRectF(0, 0, 10, 10); + maximizeRect.moveCenter(buttonRect.center().toPoint()); + + painter->save(); + + painter->setRenderHints(QPainter::Antialiasing, false); + + // Background. + painter->setPen(Qt::NoPen); + painter->setBrush(backgroundColor()); + painter->drawRect(buttonRect); + + // Foreground. + painter->setPen(foregroundColor()); + painter->setBrush(Qt::NoBrush); + + if (isChecked()) { + painter->drawPolygon(QVector { + maximizeRect.bottomLeft(), + maximizeRect.topLeft() + QPoint(0, 2), + maximizeRect.topRight() + QPointF(-2, 2), + maximizeRect.bottomRight() + QPointF(-2, 0) + }); + + painter->drawPolyline(QVector { + maximizeRect.topLeft() + QPointF(2, 2), + maximizeRect.topLeft() + QPointF(2, 0), + maximizeRect.topRight(), + maximizeRect.bottomRight() + QPointF(0, -2), + maximizeRect.bottomRight() + QPointF(-2, -2) + }); + } else { + painter->drawRect(maximizeRect); + } + + painter->restore(); +} + +QColor MaximizeButton::backgroundColor() const +{ + const auto *deco = qobject_cast(decoration()); + if (!deco) { + return {}; + } + + if (isPressed()) { + return KColorUtils::mix( + deco->titleBarBackgroundColor(), + deco->titleBarForegroundColor(), + 0.3); + } + + if (isHovered()) { + return KColorUtils::mix( + deco->titleBarBackgroundColor(), + deco->titleBarForegroundColor(), + 0.2); + } + + return deco->titleBarBackgroundColor(); +} + +QColor MaximizeButton::foregroundColor() const +{ + const auto *deco = qobject_cast(decoration()); + if (!deco) { + return {}; + } + + return deco->titleBarForegroundColor(); +} + +} // namespace Material diff --git a/src/MaximizeButton.h b/src/MaximizeButton.h new file mode 100644 index 0000000..72bc4c3 --- /dev/null +++ b/src/MaximizeButton.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +#pragma once + +// KDecoration +#include + +namespace Material +{ + +class Decoration; + +class MaximizeButton : public KDecoration2::DecorationButton +{ + Q_OBJECT + +public: + MaximizeButton(Decoration *decoration, QObject *parent = nullptr); + ~MaximizeButton() override; + + void paint(QPainter *painter, const QRect &repaintRegion) override; + +private: + QColor backgroundColor() const; + QColor foregroundColor() const; +}; + +} // namespace Material diff --git a/src/MinimizeButton.cc b/src/MinimizeButton.cc new file mode 100644 index 0000000..e50bbef --- /dev/null +++ b/src/MinimizeButton.cc @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +// own +#include "MinimizeButton.h" +#include "Decoration.h" + +// KDecoration +#include + +// KF +#include + +// Qt +#include + +namespace Material +{ + +MinimizeButton::MinimizeButton(Decoration *decoration, QObject *parent) + : DecorationButton(KDecoration2::DecorationButtonType::Minimize, decoration, parent) +{ + auto *decoratedClient = decoration->client().data(); + connect(decoratedClient, &KDecoration2::DecoratedClient::minimizeableChanged, + this, &MinimizeButton::setVisible); + + connect(this, &MinimizeButton::hoveredChanged, this, + [this] { + update(); + }); + + const int titleBarHeight = decoration->titleBarHeight(); + const QSize size(qRound(titleBarHeight * 1.33), titleBarHeight); + setGeometry(QRect(QPoint(0, 0), size)); + setVisible(decoratedClient->isMinimizeable()); +} + +MinimizeButton::~MinimizeButton() +{ +} + +void MinimizeButton::paint(QPainter *painter, const QRect &repaintRegion) +{ + Q_UNUSED(repaintRegion) + + const QRectF buttonRect = geometry(); + QRectF minimizeRect = QRectF(0, 0, 10, 10); + minimizeRect.moveCenter(buttonRect.center().toPoint()); + + painter->save(); + + painter->setRenderHints(QPainter::Antialiasing, false); + + // Background. + painter->setPen(Qt::NoPen); + painter->setBrush(backgroundColor()); + painter->drawRect(buttonRect); + + // Foreground. + painter->setPen(foregroundColor()); + painter->setBrush(Qt::NoBrush); + painter->drawLine( + minimizeRect.left(), minimizeRect.center().y(), + minimizeRect.right(), minimizeRect.center().y()); + + painter->restore(); +} + +QColor MinimizeButton::backgroundColor() const +{ + const auto *deco = qobject_cast(decoration()); + if (!deco) { + return {}; + } + + if (isPressed()) { + return KColorUtils::mix( + deco->titleBarBackgroundColor(), + deco->titleBarForegroundColor(), + 0.3); + } + + if (isHovered()) { + return KColorUtils::mix( + deco->titleBarBackgroundColor(), + deco->titleBarForegroundColor(), + 0.2); + } + + return deco->titleBarBackgroundColor(); +} + +QColor MinimizeButton::foregroundColor() const +{ + const auto *deco = qobject_cast(decoration()); + if (!deco) { + return {}; + } + + return deco->titleBarForegroundColor(); +} + +} // namespace Material diff --git a/src/MinimizeButton.h b/src/MinimizeButton.h new file mode 100644 index 0000000..0a511be --- /dev/null +++ b/src/MinimizeButton.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +#pragma once + +// KDecoration +#include + +namespace Material +{ + +class Decoration; + +class MinimizeButton : public KDecoration2::DecorationButton +{ + Q_OBJECT + +public: + MinimizeButton(Decoration *decoration, QObject *parent = nullptr); + ~MinimizeButton() override; + + void paint(QPainter *painter, const QRect &repaintRegion) override; + +private: + QColor backgroundColor() const; + QColor foregroundColor() const; +}; + +} // namespace Material diff --git a/src/material.json b/src/material.json new file mode 100644 index 0000000..8e81e9a --- /dev/null +++ b/src/material.json @@ -0,0 +1,14 @@ +{ + "KPlugin": { + "Description": "Window decoration", + "EnabledByDefault": false, + "Id": "com.github.zzag.material", + "Name": "Material", + "ServiceTypes": [ + "org.kde.kdecoration2" + ] + }, + "org.kde.kdecoration2": { + "blur": false + } +} diff --git a/src/metrics.h b/src/metrics.h new file mode 100644 index 0000000..f93da68 --- /dev/null +++ b/src/metrics.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +namespace Material +{ +namespace metrics +{ + +enum { + TitleBar_TopMargin = 1, + TitleBar_BottomMargin = TitleBar_TopMargin +}; + +} // namespace metrics +} // namespace Material diff --git a/src/plugin.cc b/src/plugin.cc new file mode 100644 index 0000000..103b939 --- /dev/null +++ b/src/plugin.cc @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 Vlad Zagorodniy + * + * 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 . + */ + +// own +#include "Decoration.h" + +// KF +#include + +K_PLUGIN_FACTORY_WITH_JSON( + MaterialDecorationFactory, + "material.json", + registerPlugin();); + +#include "plugin.moc"