feat: add KCM for Bismuth configuration

This commit is contained in:
Mikhail Zolotukhin 2021-11-01 19:23:13 +03:00
parent 3000934a96
commit 1c410cf1c7
25 changed files with 1129 additions and 5 deletions

View File

@ -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

View File

@ -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"

View File

@ -23,5 +23,3 @@ zip -qr "$KWINPKG_NAME" ./contents/ ./metadata.desktop
# Get back to the original directory
cd - > /dev/null
# TODO: Packaging for KCM

View File

@ -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"

58
src/kcm/CMakeLists.txt Normal file
View File

@ -0,0 +1,58 @@
# SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
# 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)

46
src/kcm/CMakePresets.json Normal file
View File

@ -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"
}
]
}

View File

@ -0,0 +1,3 @@
SPDX-FileCopyrightText: none
SPDX-License-Identifier: MIT

68
src/kcm/CONTRIBUTING.md Normal file
View File

@ -0,0 +1,68 @@
<!-- SPDX-FileCopyrightText: none -->
<!-- SPDX-License-Identifier: MIT -->
# 🤝 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
```

42
src/kcm/bismuth.cpp Normal file
View File

@ -0,0 +1,42 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* SPDX-License-Identifier: MIT
*/
#include "bismuth.h"
#include "bismuth_config.h"
#include <KAboutData>
#include <KLocalizedString>
#include <KPluginFactory>
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 <mail@genda.life>"));
aboutData->addAuthor(
i18nc("@info:credit", "Author"), i18nc("@info:credit", "Author"), QStringLiteral("author@domain.com"));
setAboutData(aboutData);
setButtons(Help | Apply | Default);
qmlRegisterAnonymousType<Bismuth::Config>("org.kde.bismuth.private", 1);
}
Bismuth::Config *BismuthSettings::config() const
{
return m_config;
}
#include "bismuth.moc"

26
src/kcm/bismuth.h Normal file
View File

@ -0,0 +1,26 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <KQuickAddons/ManagedConfigModule>
#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;
};

184
src/kcm/bismuth_config.kcfg Normal file
View File

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life> -->
<!-- SPDX-License-Identifier: MIT -->
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<!-- Use configuration provided by KWin Scripting -->
<kcfgfile name="kwinrc"/>
<!-- Use group name provided by the KWin Scripting -->
<group name="Script-bismuth">
<!-- This is basically copy-paste from the old configuration file -->
<entry name="debug" type="Bool">
<label>Enable logging</label>
<default>false</default>
</entry>
<entry name="enableTileLayout" type="Bool">
<label>Enable/disable Tile layout</label>
<default>true</default>
</entry>
<entry name="enableMonocleLayout" type="Bool">
<label>Enable/disable Monocle layout</label>
<default>true</default>
</entry>
<entry name="enableThreeColumnLayout" type="Bool">
<label>Enable/disable Three Column layout</label>
<default>true</default>
</entry>
<entry name="enableSpiralLayout" type="Bool">
<label>Enable/disable Spiral layout</label>
<default>true</default>
</entry>
<entry name="enableSpreadLayout" type="Bool">
<label>Enable/disable Spread layout</label>
<default>true</default>
</entry>
<entry name="enableStairLayout" type="Bool">
<label>Enable/disable Stair layout</label>
<default>true</default>
</entry>
<entry name="enableQuarterLayout" type="Bool">
<label>Enable/disable Quarter layout</label>
<default>false</default>
</entry>
<entry name="enableFloatingLayout" type="Bool">
<label>Enable/disable Floating layout</label>
<default>false</default>
</entry>
<entry name="ignoreActivity" type="String">
<label>Do not apply tiling on some activities(comma-separated list of activity names)</label>
<default></default>
</entry>
<entry name="ignoreClass" type="String">
<label>Ignore windows with certain classes(comma-separated list)</label>
<default>yakuake,spectacle,Conky</default>
</entry>
<entry name="ignoreRole" type="String">
<label>Ignore windows with specific role(comma-separated list)</label>
<default>quake</default>
</entry>
<entry name="ignoreScreen" type="String">
<label>Do not apply tiling on some screens(comma-separated list of screen number)</label>
<default></default>
</entry>
<entry name="ignoreTitle" type="String">
<label>Ignore windows with certain string in title(comma-separated list)</label>
<default></default>
</entry>
<entry name="floatingClass" type="String">
<label>Float windows with certain classes(comma-separated list)</label>
<default></default>
</entry>
<entry name="floatingTitle" type="String">
<label>Float windows with certain string in title(comma-separated list)</label>
<default></default>
</entry>
<entry name="floatUtility" type="Bool">
<label>Automatically float utility windows (Dialog, Splash, Utility)</label>
<default>true</default>
</entry>
<entry name="layoutPerActivity" type="Bool">
<label>Use separate layouts per activity.</label>
<default>true</default>
</entry>
<entry name="layoutPerDesktop" type="Bool">
<label>Use separate layouts per desktop.</label>
<default>true</default>
</entry>
<entry name="maximizeSoleTile" type="Bool">
<label>If there is only one tile on the screen, remove screen gap and its borde, making it appear almost fullscreen.</label>
<default>false</default>
</entry>
<entry name="monocleMaximize" type="Bool">
<label>Always maximize windows in Monocle layout</label>
<default>true</default>
</entry>
<entry name="monocleMinimizeRest" type="Bool">
<label>Minimize other tiles than the current one in Monocle layout</label>
<default>false</default>
</entry>
<entry name="keepFloatAbove" type="Bool">
<label>Keep tiled windows below floating windows</label>
<default>true</default>
</entry>
<entry name="noTileBorder" type="Bool">
<label>Remove borders of tiled windows</label>
<default>false</default>
</entry>
<entry name="preventMinimize" type="Bool">
<label>Prevent windows from minimizing</label>
<default>false</default>
</entry>
<entry name="preventProtrusion" type="Bool">
<label>Prevent windows from protruding out of screen</label>
<default>true</default>
</entry>
<entry name="screenGapLeft" type="Int">
<label>Gap between tiles and screen borders (Left)</label>
<default>0</default>
</entry>
<entry name="screenGapRight" type="Int">
<label>Gap between tiles and screen borders (Right)</label>
<default>0</default>
</entry>
<entry name="screenGapTop" type="Int">
<label>Gap between tiles and screen borders (Top)</label>
<default>0</default>
</entry>
<entry name="screenGapBottom" type="Int">
<label>Gap between tiles and screen borders (Bottom)</label>
<default>0</default>
</entry>
<entry name="tileLayoutGap" type="Int">
<label>Gap between tiles in Tile layout</label>
<default>0</default>
</entry>
<entry name="limitTileWidth" type="Bool">
<label>Limit the width of tiles</label>
<default>false</default>
</entry>
<entry name="limitTileWidthRatio" type="Double">
<label>Ratio of maximum tile width to the height of screen</label>
<default>1.6</default>
</entry>
<entry name="newWindowAsMaster" type="Bool">
<label>Put new window as master</label>
<default>false</default>
</entry>
</group>
</kcfg>

View File

@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
# 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

View File

@ -0,0 +1,62 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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"
}
}

View File

@ -0,0 +1,137 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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")
}
}

View File

@ -0,0 +1,25 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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
}
}

View File

@ -0,0 +1,31 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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
}
}

View File

@ -0,0 +1,25 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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
}
}

View File

@ -0,0 +1,19 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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
}

View File

@ -0,0 +1,47 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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
}

View File

@ -0,0 +1,63 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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 {}
}
}
}
}

View File

@ -0,0 +1,96 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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
}
}

View File

@ -0,0 +1,34 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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"
}
}
}

View File

@ -0,0 +1,69 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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"
}
}
}

View File

@ -0,0 +1,42 @@
/**
* SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
*
* 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
}
}
}

View File

@ -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