From 1c410cf1c759d707596a42214489dbe36bd52278 Mon Sep 17 00:00:00 2001 From: Mikhail Zolotukhin Date: Mon, 1 Nov 2021 19:23:13 +0300 Subject: [PATCH] feat: add KCM for Bismuth configuration --- scripts/build.sh | 10 + scripts/install.sh | 11 +- scripts/package.sh | 2 - scripts/uninstall.sh | 5 +- src/kcm/CMakeLists.txt | 58 ++++++ src/kcm/CMakePresets.json | 46 +++++ src/kcm/CMakePresets.json.license | 3 + src/kcm/CONTRIBUTING.md | 68 +++++++ src/kcm/bismuth.cpp | 42 ++++ src/kcm/bismuth.h | 26 +++ src/kcm/bismuth_config.kcfg | 184 ++++++++++++++++++ src/kcm/bismuth_config.kcfgc | 11 ++ src/kcm/package/contents/ui/Appearance.qml | 62 ++++++ src/kcm/package/contents/ui/Behaviour.qml | 137 +++++++++++++ .../contents/ui/components/ConfigCheckBox.qml | 25 +++ .../contents/ui/components/ConfigSpinBox.qml | 31 +++ .../ui/components/ConfigTextField.qml | 25 +++ .../ui/components/PixelsConfigSpinBox.qml | 19 ++ .../ui/components/RatioConfigSpinBox.qml | 47 +++++ src/kcm/package/contents/ui/main.qml | 63 ++++++ src/kcm/package/contents/ui/views/Layouts.qml | 96 +++++++++ .../contents/ui/views/MonocleOverlay.qml | 34 ++++ .../package/contents/ui/views/WindowRules.qml | 69 +++++++ .../contents/ui/views/WorkspaceRules.qml | 42 ++++ src/kcm/package/metadata.desktop | 18 ++ 25 files changed, 1129 insertions(+), 5 deletions(-) create mode 100644 src/kcm/CMakeLists.txt create mode 100644 src/kcm/CMakePresets.json create mode 100644 src/kcm/CMakePresets.json.license create mode 100644 src/kcm/CONTRIBUTING.md create mode 100644 src/kcm/bismuth.cpp create mode 100644 src/kcm/bismuth.h create mode 100644 src/kcm/bismuth_config.kcfg create mode 100644 src/kcm/bismuth_config.kcfgc create mode 100644 src/kcm/package/contents/ui/Appearance.qml create mode 100644 src/kcm/package/contents/ui/Behaviour.qml create mode 100644 src/kcm/package/contents/ui/components/ConfigCheckBox.qml create mode 100644 src/kcm/package/contents/ui/components/ConfigSpinBox.qml create mode 100644 src/kcm/package/contents/ui/components/ConfigTextField.qml create mode 100644 src/kcm/package/contents/ui/components/PixelsConfigSpinBox.qml create mode 100644 src/kcm/package/contents/ui/components/RatioConfigSpinBox.qml create mode 100644 src/kcm/package/contents/ui/main.qml create mode 100644 src/kcm/package/contents/ui/views/Layouts.qml create mode 100644 src/kcm/package/contents/ui/views/MonocleOverlay.qml create mode 100644 src/kcm/package/contents/ui/views/WindowRules.qml create mode 100644 src/kcm/package/contents/ui/views/WorkspaceRules.qml create mode 100644 src/kcm/package/metadata.desktop diff --git a/scripts/build.sh b/scripts/build.sh index eb88015e..322cb5d8 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -35,3 +35,13 @@ METADATA_FILE="$KWINSCRIPT_BUILDDIR/metadata.desktop" cp "$KWINSCRIPT_SOURCEDIR/res/metadata.desktop" "$METADATA_FILE" sed -i "s/\$VER/${npm_package_version:-1.0}/" "$METADATA_FILE" + +echo "๐Ÿ—๏ธ Building KCM..." + +KCM_SOURCEDIR="src/kcm" +KCM_BUILDDIR="build/kcm" + +cmake -S "$KCM_SOURCEDIR" -B "$KCM_BUILDDIR" -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +cmake --build "$KCM_BUILDDIR" +ln -sf "$PWD/$KCM_BUILDDIR/compile_commands.json" "$KCM_SOURCEDIR/compile_commands.json" # For LSP + diff --git a/scripts/install.sh b/scripts/install.sh index 984985af..db258eb0 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -6,6 +6,13 @@ set -e echo "๐Ÿ“ฆ Installing the KWin Script..." -KWINPKG_FILE="bismuth.kwinscript" -plasmapkg2 -u "$KWINPKG_FILE" > /dev/null || plasmapkg2 -i "$KWINPKG_FILE" > /dev/null +KWINSCRIPT_BUILDDIR="build/kwinscript" +KWINPKG_FILE="bismuth.kwinscript" + +plasmapkg2 -u "$KWINSCRIPT_BUILDDIR/$KWINPKG_FILE" > /dev/null || plasmapkg2 -i "$KWINSCRIPT_BUILDDIR/$KWINPKG_FILE" > /dev/null + +echo "๐Ÿ“ฆ Installing the KCM..." + +KCM_BUILDDIR="build/kcm" +sudo cmake --install "$KCM_BUILDDIR" diff --git a/scripts/package.sh b/scripts/package.sh index c542a8bd..272e7aaa 100755 --- a/scripts/package.sh +++ b/scripts/package.sh @@ -23,5 +23,3 @@ zip -qr "$KWINPKG_NAME" ./contents/ ./metadata.desktop # Get back to the original directory cd - > /dev/null -# TODO: Packaging for KCM - diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh index ae4acb0a..baea7095 100755 --- a/scripts/uninstall.sh +++ b/scripts/uninstall.sh @@ -9,5 +9,8 @@ set -e echo "๐Ÿ”ฅ Uninstalling KWin Script..." plasmapkg2 -t kwinscript -r "${npm_package_name:-Bismuth}" -# TODO: Uninstalling KCM +echo "๐Ÿ”ฅ Uninstalling KCM..." +KCM_BUILDDIR="build/kcm" + +sudo xargs rm < "$KCM_BUILDDIR/install_manifest.txt" diff --git a/src/kcm/CMakeLists.txt b/src/kcm/CMakeLists.txt new file mode 100644 index 00000000..ed645263 --- /dev/null +++ b/src/kcm/CMakeLists.txt @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.16) + +project(bismuth-kcm) + +set(QT_MIN_VERSION "5.15.0") +set(KF5_MIN_VERSION "5.80.0") + +find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) + +find_package( + Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS + Quick + Svg +) + +find_package( + KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS + I18n + KCMUtils + Declarative + Config +) + +add_library(kcm_bismuth MODULE) + +target_sources( + kcm_bismuth + PRIVATE + bismuth.cpp +) + +target_link_libraries( + kcm_bismuth + PRIVATE + Qt5::Core + KF5::CoreAddons + KF5::I18n + KF5::QuickAddons + KF5::ConfigCore + KF5::ConfigGui +) + +kconfig_add_kcfg_files(kcm_bismuth GENERATE_MOC bismuth_config.kcfgc) +kcoreaddons_desktop_to_json(kcm_bismuth "package/metadata.desktop") + +install(FILES "bismuth_config.kcfg" DESTINATION ${KDE_INSTALL_KCFGDIR}) +install(TARGETS kcm_bismuth DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms) +install(FILES package/metadata.desktop RENAME kcm_bismuth.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) + +kpackage_install_package(package kcm_bismuth kcms) diff --git a/src/kcm/CMakePresets.json b/src/kcm/CMakePresets.json new file mode 100644 index 00000000..0b7e5519 --- /dev/null +++ b/src/kcm/CMakePresets.json @@ -0,0 +1,46 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "displayName": "Default Config", + "description": "Default build using Ninja generator", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/", + "installDir": "~/.local/kde", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": { + "type": "BOOL", + "value": "ON" + } + } + }, + { + "name": "release", + "displayName": "Release Config", + "description": "Release build using Ninja generator", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/release/", + "installDir": "/usr", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "default" + }, + { + "name": "release", + "configurePreset": "release", + "configuration": "Release" + } + ] +} diff --git a/src/kcm/CMakePresets.json.license b/src/kcm/CMakePresets.json.license new file mode 100644 index 00000000..98c46251 --- /dev/null +++ b/src/kcm/CMakePresets.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: none + +SPDX-License-Identifier: MIT diff --git a/src/kcm/CONTRIBUTING.md b/src/kcm/CONTRIBUTING.md new file mode 100644 index 00000000..c9489567 --- /dev/null +++ b/src/kcm/CONTRIBUTING.md @@ -0,0 +1,68 @@ + + + +# ๐Ÿค Contributing + +We glad, that you want to contribute to the project. To make things easier please read the +following. + +## ๐Ÿ—๏ธ Development packages + +To make sure you can develop the script install all the tools you need: + +- [REUSE tool](https://git.fsfe.org/reuse/tool#install) +- [CMake](https://cmake.org/) (3.21 or later) +- [Pre-commit](https://pre-commit.com/) +- [Clang-format](https://clang.llvm.org/docs/ClangFormat.html) + +## ๐Ÿ‘ท Prepare environment + +The command will install Git pre-commit hooks you will need, such as automatic +REUSE validation and autoformatting. + +```bash +pre-commit install +``` + +Install the dependencies on your Linux Distribution: + +```bash +# Ubuntu +sudo apt install -y \ + cmake ninja-build extra-cmake-modules kirigami2-dev \ + libkf5config-dev libkf5configwidgets-dev libkf5coreaddons-dev \ + libkf5declarative-dev libkf5i18n-dev libkf5kcmutils-dev \ + libqt5svg5-dev qml-module-qtquick* qtbase5-dev \ + qtdeclarative5-dev qtquickcontrols2-5-dev g++ +``` + +```bash +# Fedora +sudo dnf install -y \ + kf5-kconfigwidgets-devel qt5-qtbase-devel qt5-qtbase-private-devel \ + qt5-qtdeclarative-devel qt5-qtquickcontrols2-devel qt5-qtsvg-devel \ + qt5-qtfeedback-devel cmake ninja-build extra-cmake-modules \ + kf5-kcmutils-devel kf5-ki18n-devel kf5-kdeclarative-devel +``` + +```bash +# OpenSUSE +sudo zypper --non-interactive install --recommends -t pattern devel_qt5 devel_C_C++ +sudo zypper --non-interactive in -y \ + ninja extra-cmake-modules kconfig-devel kcmutils-devel kdeclarative-devel \ + ki18n-devel +``` + +## ๐Ÿ› ๏ธ Building + +```bash +cmake --preset default +cmake --build --preset default +``` + +## ๐Ÿงช Testing + +```bash +source build/prefix.sh +kcmshell5 kcm_bismuth +``` diff --git a/src/kcm/bismuth.cpp b/src/kcm/bismuth.cpp new file mode 100644 index 00000000..7c32efd8 --- /dev/null +++ b/src/kcm/bismuth.cpp @@ -0,0 +1,42 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ + +#include "bismuth.h" +#include "bismuth_config.h" + +#include +#include +#include + +K_PLUGIN_CLASS_WITH_JSON(BismuthSettings, "metadata.json") + +BismuthSettings::BismuthSettings(QObject *parent, const QVariantList &args) + : KQuickAddons::ManagedConfigModule(parent, args) + , m_config(new Bismuth::Config(this)) +{ + KAboutData *aboutData = + new KAboutData(QStringLiteral("kcm_bismuth"), + i18nc("@title", "Window Tiling"), + QStringLiteral("1.0"), + QStringLiteral(""), + KAboutLicense::LicenseKey::Custom, + i18nc("@info:credit", "Copyright 2021 Mikhail Zolotukhin ")); + + aboutData->addAuthor( + i18nc("@info:credit", "Author"), i18nc("@info:credit", "Author"), QStringLiteral("author@domain.com")); + + setAboutData(aboutData); + setButtons(Help | Apply | Default); + + qmlRegisterAnonymousType("org.kde.bismuth.private", 1); +} + +Bismuth::Config *BismuthSettings::config() const +{ + return m_config; +} + +#include "bismuth.moc" diff --git a/src/kcm/bismuth.h b/src/kcm/bismuth.h new file mode 100644 index 00000000..849c9db9 --- /dev/null +++ b/src/kcm/bismuth.h @@ -0,0 +1,26 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ +#pragma once + +#include + +#include "bismuth_config.h" + +class BismuthSettings : public KQuickAddons::ManagedConfigModule +{ + Q_OBJECT + + Q_PROPERTY(Bismuth::Config *config READ config CONSTANT) + +public: + BismuthSettings(QObject *parent, const QVariantList &args); + virtual ~BismuthSettings() override = default; + + Bismuth::Config *config() const; + +private: + Bismuth::Config *m_config; +}; diff --git a/src/kcm/bismuth_config.kcfg b/src/kcm/bismuth_config.kcfg new file mode 100644 index 00000000..45cbb29e --- /dev/null +++ b/src/kcm/bismuth_config.kcfg @@ -0,0 +1,184 @@ + + + + + + + + + + + + false + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + false + + + + + false + + + + + + + + + + yakuake,spectacle,Conky + + + + + quake + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + true + + + + + true + + + + + false + + + + + true + + + + + false + + + + + true + + + + + false + + + + + false + + + + + true + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + false + + + + + 1.6 + + + + + false + + + + diff --git a/src/kcm/bismuth_config.kcfgc b/src/kcm/bismuth_config.kcfgc new file mode 100644 index 00000000..0d4bccef --- /dev/null +++ b/src/kcm/bismuth_config.kcfgc @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin +# SPDX-License-Identifier: MIT + +# See: https://api.kde.org/frameworks/kconfig/html/kconfig_compiler.html +File=bismuth_config.kcfg +NameSpace=Bismuth +ClassName=Config +Mutators=true +ParentInConstructor=true +GenerateProperties=true +UseEnumTypes=true diff --git a/src/kcm/package/contents/ui/Appearance.qml b/src/kcm/package/contents/ui/Appearance.qml new file mode 100644 index 00000000..b9696563 --- /dev/null +++ b/src/kcm/package/contents/ui/Appearance.qml @@ -0,0 +1,62 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 +import QtQuick.Layouts 1.15 + +import org.kde.kcm 1.5 as KCM +import org.kde.kirigami 2.7 as Kirigami + +import "./components" as BIC + +Kirigami.FormLayout { + id: appearanceTab + + Item { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: i18n("Outer Gaps") + } + + BIC.PixelsConfigSpinBox { + Kirigami.FormData.label: i18n("Left:") + settingName: "screenGapLeft" + } + + BIC.PixelsConfigSpinBox { + Kirigami.FormData.label: i18n("Top:") + settingName: "screenGapTop" + } + + BIC.PixelsConfigSpinBox { + Kirigami.FormData.label: i18n("Right:") + settingName: "screenGapRight" + } + + BIC.PixelsConfigSpinBox { + Kirigami.FormData.label: i18n("Bottom:") + settingName: "screenGapBottom" + } + + Item { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: "Inner Gaps" + } + + BIC.PixelsConfigSpinBox { + Kirigami.FormData.label: i18n("All:") + settingName: "tileLayoutGap" + } + + Kirigami.Separator { + Kirigami.FormData.isSection: true + } + + BIC.ConfigCheckBox { + text: i18n("No borders around tiled windows") + settingName: "noTileBorder" + } +} + diff --git a/src/kcm/package/contents/ui/Behaviour.qml b/src/kcm/package/contents/ui/Behaviour.qml new file mode 100644 index 00000000..e8d22589 --- /dev/null +++ b/src/kcm/package/contents/ui/Behaviour.qml @@ -0,0 +1,137 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 +import QtQuick.Layouts 1.15 + +import org.kde.kcm 1.5 as KCM +import org.kde.kirigami 2.7 as Kirigami + +import "./components" as BIC +import "./views" as BIView + +Kirigami.FormLayout { + id: behaviorTab + + Item { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: i18n("Layouts") + } + + BIC.ConfigCheckBox { + Kirigami.FormData.label: i18n("Separate layouts for each:") + text: i18n("Activity") + settingName: "layoutPerActivity" + } + + BIC.ConfigCheckBox { + text: i18n("Virtual Desktop") + settingName: "layoutPerDesktop" + } + + QQC2.Button { + id: configureLayoutsButton + icon.name: "document-edit" + text: i18n("Customize Layouts...") + onClicked: () => kcm.push("./views/Layouts.qml") + } + + Item { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: i18n("Windows") + } + + BIC.ConfigCheckBox { + text: i18n("Maximize sole window") + settingName: "maximizeSoleTile" + } + + BIC.ConfigCheckBox { + text: i18n("Floating windows always on top") + settingName: "keepFloatAbove" + } + + QQC2.ButtonGroup { + id: windowSpawnPositionGroup + } + + QQC2.RadioButton { + Kirigami.FormData.label: i18n("Spawn windows at:") + text: i18n("Master area") + QQC2.ButtonGroup.group: windowSpawnPositionGroup + + checked: kcm.config.newWindowAsMaster + onClicked: kcm.config.newWindowAsMaster = checked + + KCM.SettingStateBinding { + configObject: kcm.config + settingName: "newWindowAsMaster" + } + } + + QQC2.RadioButton { + text: i18n("The layout's end") + QQC2.ButtonGroup.group: windowSpawnPositionGroup + + checked: !kcm.config.newWindowAsMaster + onClicked: kcm.config.newWindowAsMaster = !checked + + KCM.SettingStateBinding { + configObject: kcm.config + settingName: "newWindowAsMaster" + } + } + + QQC2.Button { + id: windowRules + icon.name: "document-edit" + text: i18n("Window Rules...") + onClicked: () => kcm.push("./views/WindowRules.qml") + } + + Item { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: i18n("Restrictions") + } + + BIC.ConfigCheckBox { + id: restrictWidth + text: i18n("Restrict window width") + settingName: "limitTileWidth" + } + + BIC.RatioConfigSpinBox { + Kirigami.FormData.label: i18n("Window Width/Screen Height ratio:") + settingName: "limitTileWidthRatio" + + // For some reason we cannot pass a custom property to + // extraEnabledConditions, so we have to define it here. + // It is also a reason why RatioConfigSpinBox uses + // QQC2.SpinBox instead of ConfigSPinBox component + KCM.SettingStateBinding { + configObject: kcm.config + settingName: "limitTileWidthRatio" + extraEnabledConditions: restrictWidth.checked + } + } + + BIC.ConfigCheckBox { + text: i18n("Prevent window minimization") + settingName: "preventMinimize" + } + + BIC.ConfigCheckBox { + text: i18n("Prevent window from protruding from its screen") + settingName: "preventProtrusion" + } + + QQC2.Button { + id: workspaceRules + icon.name: "document-edit" + text: i18n("Workspace Rules...") + onClicked: () => kcm.push("./views/WorkspaceRules.qml") + } +} diff --git a/src/kcm/package/contents/ui/components/ConfigCheckBox.qml b/src/kcm/package/contents/ui/components/ConfigCheckBox.qml new file mode 100644 index 00000000..07c54ca1 --- /dev/null +++ b/src/kcm/package/contents/ui/components/ConfigCheckBox.qml @@ -0,0 +1,25 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 + +import org.kde.kcm 1.5 as KCM + +QQC2.CheckBox { + id: root + /** + * Name for the config option to represent + */ + property string settingName + + checked: kcm.config[settingName] + onClicked: kcm.config[settingName] = checked + + KCM.SettingStateBinding { + configObject: kcm.config + settingName: root.settingName + } +} diff --git a/src/kcm/package/contents/ui/components/ConfigSpinBox.qml b/src/kcm/package/contents/ui/components/ConfigSpinBox.qml new file mode 100644 index 00000000..d6a665b6 --- /dev/null +++ b/src/kcm/package/contents/ui/components/ConfigSpinBox.qml @@ -0,0 +1,31 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 + +import org.kde.kcm 1.5 as KCM +import org.kde.kirigami 2.7 as Kirigami + +QQC2.SpinBox { + id: root + + /** + * Name for the config option to represent + */ + property string settingName: "" + + // Implicit dimensions are set to break binding loop + implicitWidth: Kirigami.Units.gridUnit * 5 + implicitHeight: Kirigami.Units.gridUnit * 1.75 + + value: kcm.config[settingName] + onValueModified: kcm.config[settingName] = value + + KCM.SettingStateBinding { + configObject: kcm.config + settingName: root.settingName + } +} diff --git a/src/kcm/package/contents/ui/components/ConfigTextField.qml b/src/kcm/package/contents/ui/components/ConfigTextField.qml new file mode 100644 index 00000000..72659505 --- /dev/null +++ b/src/kcm/package/contents/ui/components/ConfigTextField.qml @@ -0,0 +1,25 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 + +import org.kde.kcm 1.5 as KCM + +QQC2.TextField { + id: root + /** + * Name for the config option to represent + */ + property string settingName + + text: kcm.config[settingName] + onEditingFinished: kcm.config[settingName] = text + + KCM.SettingStateBinding { + configObject: kcm.config + settingName: root.settingName + } +} diff --git a/src/kcm/package/contents/ui/components/PixelsConfigSpinBox.qml b/src/kcm/package/contents/ui/components/PixelsConfigSpinBox.qml new file mode 100644 index 00000000..fac81bdf --- /dev/null +++ b/src/kcm/package/contents/ui/components/PixelsConfigSpinBox.qml @@ -0,0 +1,19 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 +import QtQuick.Layouts 1.15 + +import org.kde.kirigami 2.7 as Kirigami + +ConfigSpinBox { + textFromValue: (value, _locale) => `${Number(value).toLocaleString(locale, 'f', 0)} px` + valueFromText: (text, locale) => Number.fromLocaleString(locale, text.replace(" px", "")) + // Pixels are always positive + from: 0 + to: 512 + editable: true +} diff --git a/src/kcm/package/contents/ui/components/RatioConfigSpinBox.qml b/src/kcm/package/contents/ui/components/RatioConfigSpinBox.qml new file mode 100644 index 00000000..1d39c0c5 --- /dev/null +++ b/src/kcm/package/contents/ui/components/RatioConfigSpinBox.qml @@ -0,0 +1,47 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 + +import org.kde.kirigami 2.7 as Kirigami +import org.kde.kcm 1.5 as KCM + +QQC2.SpinBox { + id: root + + /** + * Name for the config option to represent + */ + property string settingName: "" + property int decimals: 2 + property real realValue: value / 100 + + // Implicit dimensions are set to break binding loop + implicitWidth: Kirigami.Units.gridUnit * 5 + implicitHeight: Kirigami.Units.gridUnit * 1.75 + + from: 5 + to: 300 + stepSize: 5 + editable: true + + validator: DoubleValidator { + bottom: Math.min(root.from, root.to) + top: Math.max(root.from, root.to) + } + + // IDK why, but this is needed to make the SpinBox work + Component.onCompleted: () => { + root.value = kcm.config[settingName] * 100 + } + + value: kcm.config[settingName] * 100 + onValueModified: kcm.config[settingName] = realValue + + textFromValue: (value, locale) => Number(value / 100).toLocaleString(locale, 'f', root.decimals) + valueFromText: (text, locale) => Number.fromLocaleString(locale, text) * 100 +} + diff --git a/src/kcm/package/contents/ui/main.qml b/src/kcm/package/contents/ui/main.qml new file mode 100644 index 00000000..4639ac48 --- /dev/null +++ b/src/kcm/package/contents/ui/main.qml @@ -0,0 +1,63 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 +import QtQuick.Layouts 1.15 + +import org.kde.kirigami 2.7 as Kirigami +import org.kde.kcm 1.5 as KCM + +KCM.SimpleKCM { + id: root + KCM.ConfigModule.quickHelp: i18n("This module lets you manage various window tiling options.") + + implicitWidth: Kirigami.Units.gridUnit * 38 + implicitHeight: Kirigami.Units.gridUnit * 35 + + // TODO: replace this TabBar-plus-Frame-in-a-ColumnLayout with whatever shakes + // out of https://bugs.kde.org/show_bug.cgi?id=394296 + ColumnLayout { + id: tabLayout + spacing: 0 + + QQC2.TabBar { + id: tabBar + // Tab styles generally assume that they're touching the inner layout, + // not the frame, so we need to move the tab bar down a pixel and make + // sure it's drawn on top of the frame + z: 1 + Layout.bottomMargin: -1 + + QQC2.TabButton { + text: i18n("Appearance") + } + + QQC2.TabButton { + text: i18n("Behavior") + } + } + + QQC2.Frame { + Layout.fillWidth: true + Layout.fillHeight: true + + StackLayout { + currentIndex: tabBar.currentIndex + anchors.fill: parent + // This breaks anything inside, that is not FormLayout + // but necessary for adequate padding + anchors.topMargin: Kirigami.Units.gridUnit * -0.5 + + Appearance {} + + // For some reason QML is very British and + // refuses to load the Behavior.qml (without "u") + Behaviour {} + } + } + } +} diff --git a/src/kcm/package/contents/ui/views/Layouts.qml b/src/kcm/package/contents/ui/views/Layouts.qml new file mode 100644 index 00000000..eaf39ddb --- /dev/null +++ b/src/kcm/package/contents/ui/views/Layouts.qml @@ -0,0 +1,96 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 +import QtQuick.Layouts 1.15 + +import org.kde.kcm 1.5 as KCM +import org.kde.kirigami 2.7 as Kirigami + +import "../components" as BIC + +Kirigami.Page { + id: root + title: "Window Layouts" + + ListModel { + id: layoutsModel + ListElement { + name: "Tile" + settingName: "enableTileLayout" + } + ListElement { + name: "Monocle" + settingName: "enableMonocleLayout" + editable: true + } + ListElement { + name: "Three Column" + settingName: "enableThreeColumnLayout" + } + ListElement { + name: "Spiral" + settingName: "enableSpiralLayout" + } + ListElement { + name: "Spread" + settingName: "enableSpreadLayout" + } + ListElement { + name: "Stair" + settingName: "enableStairLayout" + } + ListElement { + name: "Quarter" + settingName: "enableQuarterLayout" + } + ListElement { + name: "Floating" + settingName: "enableFloatingLayout" + } + } + + KCM.ScrollView { + anchors.fill: parent + + view: ListView { + model: layoutsModel + + delegate: Kirigami.SwipeListItem { + contentItem: RowLayout { + RowLayout { + BIC.ConfigCheckBox { + id: layoutCheckBox + settingName: model.settingName + } + + QQC2.Label { + text: `${model.name} Layout` + } + } + } + actions: [ + Kirigami.Action { + id: editLayout + enabled: model.editable && layoutCheckBox.checked + visible: model.editable + iconName: "edit-rename" + tooltip: i18nc("@info:tooltip", "Edit Layout") + onTriggered: () => { + monocleSheet.open() + } + } + ] + } + } + } + + MonocleOverlay { + id: monocleSheet + parent: root // Without this, overlay does not work + } +} diff --git a/src/kcm/package/contents/ui/views/MonocleOverlay.qml b/src/kcm/package/contents/ui/views/MonocleOverlay.qml new file mode 100644 index 00000000..bcbac6b4 --- /dev/null +++ b/src/kcm/package/contents/ui/views/MonocleOverlay.qml @@ -0,0 +1,34 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 +import QtQuick.Layouts 1.15 + +import org.kde.kcm 1.5 as KCM +import org.kde.kirigami 2.7 as Kirigami + +import "../components" as BIC + +Kirigami.OverlaySheet { + id: monocleOverlay + + header: Kirigami.Heading { + text: i18nc("@title:window", "Monocle Layout Settings") + } + + Kirigami.FormLayout { + BIC.ConfigCheckBox { + text: i18n("Fully maximize windows (no borders, no gaps)") + settingName: "monocleMaximize" + } + + BIC.ConfigCheckBox { + text: i18n("Minimize unfocused windows") + settingName: "monocleMinimizeRest" + } + } +} diff --git a/src/kcm/package/contents/ui/views/WindowRules.qml b/src/kcm/package/contents/ui/views/WindowRules.qml new file mode 100644 index 00000000..fbd7accc --- /dev/null +++ b/src/kcm/package/contents/ui/views/WindowRules.qml @@ -0,0 +1,69 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 +import QtQuick.Layouts 1.15 + +import org.kde.kcm 1.5 as KCM +import org.kde.kirigami 2.7 as Kirigami + +import "../components" as BIC + +Kirigami.Page { + id: root + title: "Window Rules" + + Kirigami.FormLayout { + anchors.fill: parent + + Item { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: i18n("Ignore Windows") + } + + BIC.ConfigTextField { + Kirigami.FormData.label: "With classes:" + placeholderText: "Classes (comma separated)" + settingName: "ignoreClass" + implicitWidth: Kirigami.Units.gridUnit * 20 + } + + BIC.ConfigTextField { + Kirigami.FormData.label: "With titles:" + placeholderText: "Titles (comma separated)" + settingName: "ignoreTitle" + } + + BIC.ConfigTextField { + Kirigami.FormData.label: "With roles:" + placeholderText: "Roles (comma separated)" + settingName: "ignoreRole" + } + + Item { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: i18n("Float Windows") + } + + BIC.ConfigTextField { + Kirigami.FormData.label: "With classes:" + placeholderText: "Classes (comma separated)" + settingName: "floatingClass" + } + + BIC.ConfigTextField { + Kirigami.FormData.label: "With titles:" + placeholderText: "Titles (comma separated)" + settingName: "floatingTitle" + } + + BIC.ConfigCheckBox { + text: "With utility roles" + settingName: "floatUtility" + } + } +} diff --git a/src/kcm/package/contents/ui/views/WorkspaceRules.qml b/src/kcm/package/contents/ui/views/WorkspaceRules.qml new file mode 100644 index 00000000..7bcf4c1e --- /dev/null +++ b/src/kcm/package/contents/ui/views/WorkspaceRules.qml @@ -0,0 +1,42 @@ +/** + * SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin + * + * SPDX-License-Identifier: MIT + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QQC2 +import QtQuick.Layouts 1.15 + +import org.kde.kcm 1.5 as KCM +import org.kde.kirigami 2.7 as Kirigami + +import "../components" as BIC + +Kirigami.Page { + id: root + title: "Workspace Rules" + + Kirigami.FormLayout { + anchors.fill: parent + + Item { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: i18n("Disable Tiling") + } + + BIC.ConfigTextField { + Kirigami.FormData.label: "On Activities:" + placeholderText: "Activities names (comma separated)" + settingName: "ignoreActivity" + implicitWidth: Kirigami.Units.gridUnit * 20 + } + + BIC.ConfigTextField { + Kirigami.FormData.label: "On Screens:" + placeholderText: "Screen numbers (comma separated)" + settingName: "ignoreScreen" + // implicitWidth: Kirigami.Units.gridUnit * 20 + } + } +} diff --git a/src/kcm/package/metadata.desktop b/src/kcm/package/metadata.desktop new file mode 100644 index 00000000..96cead5c --- /dev/null +++ b/src/kcm/package/metadata.desktop @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: none +# +# SPDX-License-Identifier: MIT + +[Desktop Entry] +Name=Window Tiling +Comment=Configure Window Tiling specific options +Encoding=UTF-8 +Type=Service +Icon=bismuth +X-KDE-Library=kcm_bismuth +X-KDE-ServiceTypes=KCModule +X-KDE-FormFactors=desktop,handset,tablet,mediacenter +X-Plasma-MainScript=ui/main.qml +X-KDE-System-Settings-Parent-Category=windowmanagement +X-KDE-Keywords=KWin,Script,Tiling,Bismuth,Window,WM,Layout,Gaps,Borders,Rules,Minimization,Float +X-KDE-ParentApp=kcontrol +X-KDE-PluginInfo-Name=kcm_bismuth