Very early KDE 6 release.

This commit is contained in:
wackyideas 2024-08-09 03:20:25 +02:00
parent 7cc4ccabbc
commit 686046d4f7
6272 changed files with 140920 additions and 529657 deletions

View file

@ -0,0 +1,228 @@
/*
SPDX-FileCopyrightText: 2018 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Controls 2.4 as QQC2
import QtQuick.Layouts 1.3
import org.kde.plasma.plasmoid 2.0
import org.kde.kirigami 2.20 as Kirigami
import org.kde.kcmutils as KCM
/**
* A copy of Kirigami.AboutPage adapted to KPluginMetadata instead of KAboutData
*/
KCM.SimpleKCM {
id: page
title: i18n("About")
property var metaData: Plasmoid.metaData
Component {
id: personDelegate
RowLayout {
height: implicitHeight + (Kirigami.Units.smallSpacing * 2)
spacing: Kirigami.Units.smallSpacing * 2
Kirigami.Icon {
width: Kirigami.Units.iconSizes.smallMedium
height: width
source: "user"
}
QQC2.Label {
text: modelData.name
textFormat: Text.PlainText
}
Row {
// Group action buttons together
spacing: 0
QQC2.ToolButton {
visible: modelData.emailAddress
width: height
icon.name: "mail-sent"
display: QQC2.AbstractButton.IconOnly
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Send an email to %1", modelData.emailAddress)
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.text: text
onClicked: Qt.openUrlExternally("mailto:%1".arg(modelData.emailAddress))
}
QQC2.ToolButton {
visible: modelData.webAddress
width: height
icon.name: "globe"
display: QQC2.AbstractButton.IconOnly
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip %1 url", "Open website %1", modelData.webAddress)
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.text: modelData.webAddress
onClicked: Qt.openUrlExternally(modelData.webAddress)
}
}
}
}
Component {
id: licenseComponent
Kirigami.OverlaySheet {
property alias text: licenseLabel.text
onClosed: destroy()
Kirigami.SelectableLabel {
id: licenseLabel
implicitWidth: Math.max(Kirigami.Units.gridUnit * 25, Math.round(page.width / 2), contentWidth)
wrapMode: Text.WordWrap
}
Component.onCompleted: open();
}
}
Item {
height: childrenRect.height
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Kirigami.Units.largeSpacing
GridLayout {
columns: 2
Layout.fillWidth: true
Kirigami.Icon {
Layout.rowSpan: 2
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
Layout.preferredWidth: height
Layout.maximumWidth: page.width / 3;
Layout.rightMargin: Kirigami.Units.largeSpacing
source: page.metaData.iconName || page.metaData.pluginId
fallback: "application-x-plasma"
}
Kirigami.Heading {
Layout.fillWidth: true
text: page.metaData.name + " " + page.metaData.version
textFormat: Text.PlainText
}
Kirigami.Heading {
Layout.fillWidth: true
Layout.maximumWidth: Kirigami.Units.gridUnit * 15
level: 2
wrapMode: Text.WordWrap
text: page.metaData.description
textFormat: Text.PlainText
}
}
Kirigami.Separator {
Layout.fillWidth: true
}
Kirigami.Heading {
Layout.topMargin: Kirigami.Units.smallSpacing
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Copyright")
textFormat: Text.PlainText
}
ColumnLayout {
spacing: Kirigami.Units.smallSpacing
Layout.leftMargin: Kirigami.Units.smallSpacing
QQC2.Label {
text: page.metaData.copyrightText
textFormat: Text.PlainText
visible: text.length > 0
}
Kirigami.UrlButton {
url: page.metaData.website
visible: url.length > 0
}
RowLayout {
spacing: Kirigami.Units.smallSpacing
QQC2.Label {
text: i18nd("plasma_shell_org.kde.plasma.desktop", "License:")
textFormat: Text.PlainText
}
Kirigami.LinkButton {
text: page.metaData.license
Accessible.description: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:whatsthis", "View license text")
onClicked: {
licenseComponent.incubateObject(page.Window.window.contentItem, {
"text": page.metaData.licenseText,
"title": page.metaData.license,
}, Qt.Asynchronous);
}
}
}
}
Kirigami.Heading {
Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.smallSpacing
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Authors")
textFormat: Text.PlainText
visible: page.metaData.authors.length > 0
}
Repeater {
model: page.metaData.authors
delegate: personDelegate
}
Kirigami.Heading {
height: visible ? implicitHeight : 0
Layout.topMargin: Kirigami.Units.smallSpacing
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Credits")
textFormat: Text.PlainText
visible: repCredits.count > 0
}
Repeater {
id: repCredits
model: page.metaData.otherContributors
delegate: personDelegate
}
Kirigami.Heading {
height: visible ? implicitHeight : 0
Layout.topMargin: Kirigami.Units.smallSpacing
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Translators")
textFormat: Text.PlainText
visible: repTranslators.count > 0
}
Repeater {
id: repTranslators
model: page.metaData.translators
delegate: personDelegate
}
Item {
Layout.fillWidth: true
}
QQC2.Button {
Layout.alignment: Qt.AlignHCenter
icon.name: "tools-report-bug"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Report a Bug…")
visible: page.metaData.bugReportUrl.length > 0
onClicked: Qt.openUrlExternally(page.metaData.bugReportUrl)
}
}
}
}

View file

@ -0,0 +1,514 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2020 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
SPDX-FileCopyrightText: 2022-2023 ivan tkachenko <me@ratijas.tk>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami as Kirigami
import org.kde.kitemmodels 1.0 as KItemModels
import org.kde.plasma.configuration 2.0
import org.kde.plasma.plasmoid 2.0
Rectangle {
id: root
implicitWidth: Kirigami.Units.gridUnit * 40
implicitHeight: Kirigami.Units.gridUnit * 30
Layout.minimumWidth: Kirigami.Units.gridUnit * 30
Layout.minimumHeight: Kirigami.Units.gridUnit * 21
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
LayoutMirroring.childrenInherit: true
color: Kirigami.Theme.backgroundColor
property bool isContainment: false
property ConfigModel globalConfigModel: globalAppletConfigModel
property url currentSource
function closing() {
if (applyButton.enabled) {
messageDialog.item = null;
messageDialog.open();
return false;
}
return true;
}
function saveConfig() {
const config = Plasmoid.configuration; // type: KConfigPropertyMap
config.keys().forEach(key => {
const cfgKey = "cfg_" + key;
if (cfgKey in app.pageStack.currentItem) {
config[key] = app.pageStack.currentItem[cfgKey];
}
})
plasmoid.configuration.writeConfig();
// For ConfigurationContainmentActions.qml
if (app.pageStack.currentItem.hasOwnProperty("saveConfig")) {
app.pageStack.currentItem.saveConfig()
}
}
Connections {
target: configDialog
function onClosing(event) {
event.accepted = closing();
}
}
ConfigModel {
id: globalAppletConfigModel
ConfigCategory {
name: i18nd("plasma_shell_org.kde.plasma.desktop", "Keyboard Shortcuts")
icon: "preferences-desktop-keyboard"
source: Qt.resolvedUrl("ConfigurationShortcuts.qml")
}
}
KItemModels.KSortFilterProxyModel {
id: configDialogFilterModel
sourceModel: configDialog.configModel
filterRowCallback: (row, parent) => {
return sourceModel.data(sourceModel.index(row, 0), ConfigModel.VisibleRole);
}
}
function settingValueChanged() {
applyButton.enabled = true;
}
function pushReplace(item, config) {
let page;
if (app.pageStack.depth === 0) {
page = app.pageStack.push(item, config);
} else {
page = app.pageStack.replace(item, config);
}
app.currentConfigPage = page;
}
Component {
id: configurationKcmPageComponent
ConfigurationKcmPage {
}
}
function open(item) {
app.isAboutPage = false;
root.currentSource = item.source
if (item.source) {
app.isAboutPage = item.source === Qt.resolvedUrl("AboutPlugin.qml");
if (isContainment) {
pushReplace(Qt.resolvedUrl("ConfigurationAppletPage.qml"), {configItem: item});
} else {
const config = Plasmoid.configuration; // type: KConfigPropertyMap
const props = {
"title": item.name
};
config.keys().forEach(key => {
props["cfg_" + key] = config[key];
});
pushReplace(item.source, props);
}
} else if (item.kcm) {
pushReplace(configurationKcmPageComponent, {kcm: item.kcm, internalPage: item.kcm.mainUi});
} else {
app.pageStack.pop();
}
applyButton.enabled = false
}
Connections {
target: app.currentConfigPage
function onSettingValueChanged() {
applyButton.enabled = true;
}
}
Connections {
target: app.pageStack
function onCurrentItemChanged() {
if (app.pageStack.currentItem !== null && !isContainment) {
const config = Plasmoid.configuration; // type: KConfigPropertyMap
config.keys().forEach(key => {
const changedSignal = app.pageStack.currentItem["cfg_" + key + "Changed"];
if (changedSignal) {
changedSignal.connect(() => root.settingValueChanged());
}
});
const configurationChangedSignal = app.pageStack.currentItem.configurationChanged;
if (configurationChangedSignal) {
configurationChangedSignal.connect(() => root.settingValueChanged());
}
}
}
}
Component.onCompleted: {
// if we are a containment then the first item will be ConfigurationContainmentAppearance
// if the applet does not have own configs then the first item will be Shortcuts
if (isContainment || !configDialog.configModel || configDialog.configModel.count === 0) {
open(root.globalConfigModel.get(0))
} else {
open(configDialog.configModel.get(0))
}
}
function applicationWindow() {
return app;
}
QQC2.ScrollView {
id: categoriesScroll
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
width: Kirigami.Units.gridUnit * 10
contentWidth: availableWidth
Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false
activeFocusOnTab: true
focus: true
Accessible.role: Accessible.MenuBar
background: Rectangle {
color: Kirigami.Theme.backgroundColor
}
Image {
source: "rsrc/bg.png"
anchors.fill: parent
smooth: true
}
Keys.onUpPressed: event => {
const buttons = categories.children
let foundPrevious = false
for (let i = buttons.length - 1; i >= 0; --i) {
const button = buttons[i];
if (!button.hasOwnProperty("highlighted")) {
// not a ConfigCategoryDelegate
continue;
}
if (foundPrevious) {
categories.openCategory(button.item)
categoriesScroll.forceActiveFocus(Qt.TabFocusReason)
return
} else if (button.highlighted) {
foundPrevious = true
}
}
event.accepted = false
}
Keys.onDownPressed: event => {
const buttons = categories.children
let foundNext = false
for (let i = 0, length = buttons.length; i < length; ++i) {
const button = buttons[i];
if (!button.hasOwnProperty("highlighted")) {
continue;
}
if (foundNext) {
categories.openCategory(button.item)
categoriesScroll.forceActiveFocus(Qt.TabFocusReason)
return
} else if (button.highlighted) {
foundNext = true
}
}
event.accepted = false
}
ColumnLayout {
id: categories
spacing: Kirigami.Units.largeSpacing
width: categoriesScroll.contentWidth
focus: true
function openCategory(item) {
if (applyButton.enabled) {
messageDialog.item = item;
messageDialog.open();
return;
}
open(item)
}
Item {
id: paddingItem
Layout.preferredHeight: Kirigami.Units.iconSizes.small
}
Component {
id: categoryDelegate
RowLayout {
id: delegate
required property var model
property var item: model
Accessible.role: Accessible.MenuItem
Accessible.name: model.name
Accessible.description: i18nd("plasma_shell_org.kde.plasma.desktop", "Open configuration page")
Accessible.onPressAction: ma.clicked(null)
Layout.leftMargin: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.largeSpacing
property bool highlighted: {
if (app.pageStack.currentItem) {
if (model.kcm && app.pageStack.currentItem.kcm) {
return model.kcm == app.pageStack.currentItem.kcm
} else {
return root.currentSource == model.source
}
}
return false
}
Kirigami.Icon {
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
implicitWidth: Kirigami.Units.iconSizes.small
implicitHeight: Kirigami.Units.iconSizes.small
source: delegate.model.icon
}
Text {
color: ma.containsMouse ? "#074ae5" : "#151c55"
font.underline: ma.containsMouse
font.bold: parent.highlighted || delegate.focus
text: parent.model.name
wrapMode: Text.WordWrap
MouseArea {
id: ma
anchors.fill: parent
enabled: !highlighted
hoverEnabled: true
onClicked: categories.openCategory(model);
cursorShape: delegate.highlighted ? Qt.ArrowCursor : Qt.PointingHandCursor
}
}
}
/*ConfigCategoryDelegate {
id: delegate
onActivated: categories.openCategory(model);
highlighted: {
if (app.pageStack.currentItem) {
if (model.kcm && app.pageStack.currentItem.kcm) {
return model.kcm == app.pageStack.currentItem.kcm
} else {
return root.currentSource == model.source
}
}
return false
}
item: model
}*/
}
Repeater {
Layout.fillWidth: true
model: root.isContainment ? globalConfigModel : undefined
delegate: categoryDelegate
}
Repeater {
Layout.fillWidth: true
model: configDialogFilterModel
delegate: categoryDelegate
}
Repeater {
Layout.fillWidth: true
model: !root.isContainment ? globalConfigModel : undefined
delegate: categoryDelegate
}
Repeater {
Layout.fillWidth: true
model: ConfigModel {
ConfigCategory{
name: i18nd("plasma_shell_org.kde.plasma.desktop", "About")
icon: "help-about"
source: Qt.resolvedUrl("AboutPlugin.qml")
}
}
delegate: categoryDelegate
}
}
}
Kirigami.Separator {
anchors {
left: parent.left
right: parent.right
top: parent.top
}
z: 1
}
Kirigami.Separator {
id: verticalSeparator
anchors {
top: parent.top
left: categoriesScroll.right
bottom: parent.bottom
}
z: 1
}
Kirigami.ApplicationItem {
id: app
anchors {
top: parent.top
left: verticalSeparator.right
right: parent.right
bottom: parent.bottom
}
pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.Breadcrumb
wideScreen: true
pageStack.globalToolBar.separatorVisible: bottomSeparator.visible
pageStack.globalToolBar.colorSet: Kirigami.Theme.View
property var currentConfigPage: null
onCurrentConfigPageChanged: {
if(currentConfigPage) {
currentConfigPage.Kirigami.Theme.colorSet = Kirigami.Theme.View
}
}
property bool isAboutPage: false
Kirigami.PromptDialog {
id: messageDialog
property var item
title: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply Settings")
subtitle: i18nd("plasma_shell_org.kde.plasma.desktop", "The settings of the current module have changed. Do you want to apply the changes or discard them?")
standardButtons: Kirigami.Dialog.Apply | Kirigami.Dialog.Discard | Kirigami.Dialog.Cancel
onApplied: {
applyAction.trigger()
discarded();
}
onDiscarded: {
if (item) {
root.open(item);
messageDialog.close();
} else {
applyButton.enabled = false;
configDialog.close();
}
}
}
footer: QQC2.Pane {
padding: Kirigami.Units.largeSpacing
contentItem: RowLayout {
id: buttonsRow
spacing: Kirigami.Units.smallSpacing
Kirigami.Theme.colorSet: Kirigami.Theme.View
Item {
Layout.fillWidth: true
}
QQC2.Button {
icon.name: "dialog-ok"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "OK")
onClicked: acceptAction.trigger()
KeyNavigation.tab: categories
}
QQC2.Button {
id: applyButton
enabled: false
icon.name: "dialog-ok-apply"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply")
visible: !app.isAboutPage && app.pageStack.currentItem && (!app.pageStack.currentItem.kcm || app.pageStack.currentItem.kcm.buttons & 4) // 4 = Apply button
onClicked: applyAction.trigger()
}
QQC2.Button {
icon.name: "dialog-cancel"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Cancel")
onClicked: cancelAction.trigger()
visible: !app.isAboutPage
}
}
background: Item {
Kirigami.Separator {
id: bottomSeparator
visible: app.pageStack.currentItem
&& app.pageStack.currentItem.flickable
&& !(app.pageStack.currentItem.flickable.atYBeginning
&& app.pageStack.currentItem.flickable.atYEnd)
anchors {
left: parent.left
right: parent.right
top: parent.top
}
}
}
}
QQC2.Action {
id: acceptAction
onTriggered: {
applyAction.trigger();
configDialog.close();
}
}
QQC2.Action {
id: applyAction
onTriggered: {
if (isContainment) {
app.pageStack.get(0).saveConfig()
} else {
root.saveConfig()
}
applyButton.enabled = false;
}
}
QQC2.Action {
id: cancelAction
onTriggered: {
if (root.closing()) {
configDialog.close();
}
}
}
Keys.onReturnPressed: acceptAction.trigger();
Keys.onEscapePressed: cancelAction.trigger();
}
}

View file

@ -0,0 +1,71 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.3 as QQC2
import QtQuick.Window 2.2
import org.kde.kquickcontrolsaddons 2.0
import org.kde.kirigami 2.5 as Kirigami
QQC2.ItemDelegate {
id: delegate
signal activated()
//BEGIN properties
Layout.fillWidth: true
hoverEnabled: true
Accessible.role: Accessible.MenuItem
Accessible.name: model.name
Accessible.description: i18nd("plasma_shell_org.kde.plasma.desktop", "Open configuration page")
Accessible.onPressAction: delegate.clicked()
property var item
//END properties
//BEGIN connections
onClicked: {
if (highlighted) {
return;
}
activated()
}
//END connections
//BEGIN UI components
contentItem: ColumnLayout {
id: delegateContents
spacing: Kirigami.Units.smallSpacing
Kirigami.Icon {
Layout.alignment: Qt.AlignHCenter
implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium
source: model.icon
selected: Window.active && (delegate.highlighted || delegate.pressed)
}
QQC2.Label {
id: nameLabel
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.smallSpacing
text: model.name
textFormat: Text.PlainText
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
color: Window.active && (delegate.highlighted || delegate.pressed) ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor
font.bold: delegate.highlighted && delegate.parent.activeFocus
Accessible.ignored: true
}
}
//END UI components
}

View file

@ -0,0 +1,81 @@
/*
SPDX-FileCopyrightText: 2020 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.0
import org.kde.plasma.plasmoid 2.0
import org.kde.kirigami 2.10 as Kirigami
Kirigami.ScrollablePage {
id: root
title: configItem.name
required property var configItem
signal settingValueChanged()
function saveConfig() {
const config = Plasmoid.configuration; // type: KConfigPropertyMap
config.keys().forEach(key => {
const cfgKey = "cfg_" + key;
if (cfgKey in loader.item) {
config[key] = loader.item[cfgKey];
}
})
Plasmoid.configuration.writeConfig();
// For ConfigurationContainmentActions.qml
if (loader.item.hasOwnProperty("saveConfig")) {
loader.item.saveConfig()
}
}
implicitHeight: loader.height
padding: configItem.includeMargins ? Kirigami.Units.largeSpacing : 0
bottomPadding: 0
Loader {
id: loader
width: parent.width
// HACK the height of the loader is based on the implicitHeight of the content.
// Unfortunately not all content items have a sensible implicitHeight.
// If it is zero fall back to the height of its children
// Also make it at least as high as the page itself. Some existing configs assume they fill the whole space
// TODO KF6 clean this up by making all configs based on SimpleKCM/ScrollViewKCM/GridViewKCM
height: Math.max(root.availableHeight, item.implicitHeight ? item.implicitHeight : item.childrenRect.height)
Component.onCompleted: {
const config = Plasmoid.configuration; // type: KConfigPropertyMap
const props = {};
config.keys().forEach(key => {
props["cfg_" + key] = config[key];
});
setSource(configItem.source, props);
}
onLoaded: {
const config = Plasmoid.configuration; // type: KConfigPropertyMap
config.keys().forEach(key => {
const changedSignal = item["cfg_" + key + "Changed"];
if (changedSignal) {
changedSignal.connect(() => root.settingValueChanged());
}
});
const configurationChangedSignal = item.configurationChanged;
if (configurationChangedSignal) {
configurationChangedSignal.connect(() => root.settingValueChanged());
}
}
}
}

View file

@ -0,0 +1,190 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick
import QtQuick.Controls 2.3 as QQC2
import QtQuick.Layouts 1.0
import org.kde.kirigami 2.20 as Kirigami
Item {
id: root
signal configurationChanged
implicitWidth: mainColumn.implicitWidth
implicitHeight: mainColumn.implicitHeight
property var prettyStrings: {
"LeftButton": i18nd("plasma_shell_org.kde.plasma.desktop", "Left-Button"),
"RightButton": i18nd("plasma_shell_org.kde.plasma.desktop", "Right-Button"),
"MiddleButton": i18nd("plasma_shell_org.kde.plasma.desktop", "Middle-Button"),
"BackButton": i18nd("plasma_shell_org.kde.plasma.desktop", "Back-Button"),
"ForwardButton": i18nd("plasma_shell_org.kde.plasma.desktop", "Forward-Button"),
"wheel:Vertical": i18nd("plasma_shell_org.kde.plasma.desktop", "Vertical-Scroll"),
"wheel:Horizontal": i18nd("plasma_shell_org.kde.plasma.desktop", "Horizontal-Scroll"),
"ShiftModifier": i18nd("plasma_shell_org.kde.plasma.desktop", "Shift"),
"ControlModifier": i18nd("plasma_shell_org.kde.plasma.desktop", "Ctrl"),
"AltModifier": i18nd("plasma_shell_org.kde.plasma.desktop", "Alt"),
"MetaModifier": i18nd("plasma_shell_org.kde.plasma.desktop", "Meta")
}
function saveConfig() {
configDialog.currentContainmentActionsModel.save();
}
Connections {
target: configDialog.currentContainmentActionsModel
function onConfigurationChanged() {
root.configurationChanged()
}
}
Component {
id: aboutComponent
Kirigami.OverlaySheet {
id: internalAboutDialog
property alias metaData: aboutPluginPage.metaData
width: Math.round(root.width * 0.8)
onClosed: destroy()
AboutPlugin {
id: aboutPluginPage
metaData: internalAboutDialog.metaData
}
Component.onCompleted: open();
}
}
GridLayout {
id: mainColumn
flow: GridLayout.TopToBottom
y: 25
width: parent.width
Repeater {
id: actionsRepeater
model: configDialog.currentContainmentActionsModel
MouseEventInputButton {
Layout.column: 0
Layout.row: index
Layout.fillWidth: true
Layout.minimumWidth: implicitWidth
defaultText: {
var splitAction = model.action.split(';');
var button = splitAction[0];
var modifiers = (splitAction[1] || "").split('|').filter(function (item) {
return item !== "NoModifier";
});
var parts = modifiers;
modifiers.push(button);
return parts.map(function (item) {
return prettyStrings[item] || item;
}).join(i18ndc("plasma_shell_org.kde.plasma.desktop", "Concatenation sign for shortcuts, e.g. Ctrl+Shift", "+"));
}
eventString: model.action
onEventStringChanged: {
configDialog.currentContainmentActionsModel.update(index, eventString, model.pluginName);
}
}
}
Repeater {
model: configDialog.currentContainmentActionsModel
QQC2.ComboBox {
id: pluginsCombo
// "index" argument of onActivated shadows the model index
readonly property int pluginIndex: index
Layout.fillWidth: true
Layout.column: 1
Layout.row: index
// both MouseEventInputButton and this ComboBox have fillWidth for a uniform layout
// however, their implicit sizes is taken into account and they compete against
// each other for available space. By setting an insane preferredWidth we give
// ComboBox a greater share of the available space
Layout.preferredWidth: 9000
model: configDialog.containmentActionConfigModel
textRole: "name"
property bool initialized: false
Component.onCompleted: {
for (var i = 0; i < configDialog.containmentActionConfigModel.count; ++i) {
if (configDialog.containmentActionConfigModel.get(i).pluginName === pluginName) {
pluginsCombo.currentIndex = i;
break;
}
}
pluginsCombo.initialized = true;
}
onActivated: {
if (initialized) {
var newPluginName = configDialog.containmentActionConfigModel.get(index).pluginName;
if (newPluginName !== pluginName) {
configDialog.currentContainmentActionsModel.update(pluginIndex, action, newPluginName);
}
}
}
}
}
Repeater {
model: configDialog.currentContainmentActionsModel
RowLayout {
Layout.column: 2
Layout.row: index
QQC2.Button {
icon.name: "configure"
width: height
enabled: model.hasConfigurationInterface
onClicked: {
configDialog.currentContainmentActionsModel.showConfiguration(index, this);
}
}
QQC2.Button {
icon.name: "dialog-information"
width: height
onClicked: {
const metaData = configDialog.currentContainmentActionsModel.aboutMetaData(index);
if (!metaData) {
return;
}
aboutComponent.incubateObject(root.Window.window.contentItem, {
"metaData": metaData,
"title": i18ndc("plasma_shell_org.kde.plasma.desktop", "@title", "About"),
}, Qt.Asynchronous);
}
}
QQC2.Button {
icon.name: "list-remove"
width: height
onClicked: {
configDialog.currentContainmentActionsModel.remove(index);
}
}
}
}
MouseEventInputButton {
defaultText: i18nd("plasma_shell_org.kde.plasma.desktop", "Add Action");
icon.name: checked ? "input-mouse-symbolic" : "list-add"
onEventStringChanged: {
configDialog.currentContainmentActionsModel.append(eventString, "org.kde.contextmenu");
}
}
}
}

View file

@ -0,0 +1,213 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import org.kde.plasma.configuration 2.0
import QtQuick.Controls 2.3 as QQC2
import QtQuick.Layouts 1.1
import QtQml 2.15
import org.kde.newstuff 1.62 as NewStuff
import org.kde.kirigami 2.20 as Kirigami
import org.kde.kcmutils
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.configuration 2.0
Item {
id: appearanceRoot
signal configurationChanged
property int formAlignment: wallpaperComboBox.Kirigami.ScenePosition.x - appearanceRoot.Kirigami.ScenePosition.x + Kirigami.Units.smallSpacing
property string currentWallpaper: ""
property string containmentPlugin: ""
property alias parentLayout: parentLayout
function saveConfig() {
if (main.currentItem.saveConfig) {
main.currentItem.saveConfig()
}
configDialog.currentWallpaper = appearanceRoot.currentWallpaper;
for (var key in configDialog.wallpaperConfiguration) {
if (main.currentItem["cfg_"+key] !== undefined) {
configDialog.wallpaperConfiguration[key] = main.currentItem["cfg_"+key]
}
}
configDialog.applyWallpaper()
configDialog.containmentPlugin = appearanceRoot.containmentPlugin
}
ColumnLayout {
width: root.availableWidth
height: Math.max(implicitHeight, root.availableHeight)
spacing: 0 // unless it's 0 there will be an additional gap between two FormLayouts
Component.onCompleted: {
for (var i = 0; i < configDialog.containmentPluginsConfigModel.count; ++i) {
var pluginName = configDialog.containmentPluginsConfigModel.data(configDialog.containmentPluginsConfigModel.index(i, 0), ConfigModel.PluginNameRole);
if (configDialog.containmentPlugin === pluginName) {
pluginComboBox.currentIndex = i
pluginComboBox.activated(i);
break;
}
}
for (var i = 0; i < configDialog.wallpaperConfigModel.count; ++i) {
var pluginName = configDialog.wallpaperConfigModel.data(configDialog.wallpaperConfigModel.index(i, 0), ConfigModel.PluginNameRole);
if (configDialog.currentWallpaper === pluginName) {
wallpaperComboBox.currentIndex = i
wallpaperComboBox.activated(i);
break;
}
}
}
Kirigami.InlineMessage {
visible: Plasmoid.immutable || animating
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Layout changes have been restricted by the system administrator")
showCloseButton: true
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing * 2 // we need this because ColumnLayout's spacing is 0
}
Kirigami.FormLayout {
id: parentLayout // needed for twinFormLayouts to work in wallpaper plugins
twinFormLayouts: main.currentItem.formLayout || []
Layout.fillWidth: true
QQC2.ComboBox {
id: pluginComboBox
Layout.preferredWidth: Math.max(implicitWidth, wallpaperComboBox.implicitWidth)
Kirigami.FormData.label: i18nd("plasma_shell_org.kde.plasma.desktop", "Layout:")
enabled: !Plasmoid.immutable
model: configDialog.containmentPluginsConfigModel
textRole: "name"
onActivated: {
var model = configDialog.containmentPluginsConfigModel.get(currentIndex)
appearanceRoot.containmentPlugin = model.pluginName
appearanceRoot.configurationChanged()
}
}
RowLayout {
Layout.fillWidth: true
enabled: main.currentItem.objectName !== "switchContainmentWarningItem"
Kirigami.FormData.label: i18nd("plasma_shell_org.kde.plasma.desktop", "Wallpaper type:")
QQC2.ComboBox {
id: wallpaperComboBox
Layout.preferredWidth: Math.max(implicitWidth, pluginComboBox.implicitWidth)
model: configDialog.wallpaperConfigModel
textRole: "name"
onActivated: {
var idx = configDialog.wallpaperConfigModel.index(currentIndex, 0)
var pluginName = configDialog.wallpaperConfigModel.data(idx, ConfigModel.PluginNameRole)
if (appearanceRoot.currentWallpaper === pluginName) {
return;
}
appearanceRoot.currentWallpaper = pluginName
configDialog.currentWallpaper = pluginName
main.sourceFile = configDialog.wallpaperConfigModel.data(idx, ConfigModel.SourceRole)
appearanceRoot.configurationChanged()
}
}
NewStuff.Button {
configFile: "wallpaperplugin.knsrc"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Get New Plugins…")
visibleWhenDisabled: true // don't hide on disabled
Layout.preferredHeight: wallpaperComboBox.height
}
}
}
Item {
id: emptyConfig
}
QQC2.StackView {
id: main
implicitHeight: main.empty ? 0 : currentItem.implicitHeight
Layout.fillHeight: true;
Layout.fillWidth: true;
// Bug 360862: if wallpaper has no config, sourceFile will be ""
// so we wouldn't load emptyConfig and break all over the place
// hence set it to some random value initially
property string sourceFile: "tbd"
onSourceFileChanged: loadSourceFile()
function loadSourceFile() {
const wallpaperConfig = configDialog.wallpaperConfiguration
// BUG 407619: wallpaperConfig can be null before calling `ContainmentItem::loadWallpaper()`
if (wallpaperConfig && sourceFile) {
var props = {
"configDialog": configDialog
}
for (var key in wallpaperConfig) {
props["cfg_" + key] = wallpaperConfig[key]
}
var newItem = replace(Qt.resolvedUrl(sourceFile), props)
for (var key in wallpaperConfig) {
var changedSignal = newItem["cfg_" + key + "Changed"]
if (changedSignal) {
changedSignal.connect(appearanceRoot.configurationChanged)
}
}
const configurationChangedSignal = newItem.configurationChanged
if (configurationChangedSignal) {
configurationChangedSignal.connect(appearanceRoot.configurationChanged)
}
} else {
replace(emptyConfig)
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
}
}
Component {
id: switchContainmentWarning
Item {
objectName: "switchContainmentWarningItem"
Kirigami.PlaceholderMessage {
id: message
width: parent.width - Kirigami.Units.largeSpacing * 8
anchors.centerIn: parent
icon.name: "documentinfo"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Layout changes must be applied before other changes can be made")
helpfulAction: QQC2.Action {
icon.name: "dialog-ok-apply"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply Now")
onTriggered: saveConfig()
}
}
}
}
onContainmentPluginChanged: {
if (configDialog.containmentPlugin !== appearanceRoot.containmentPlugin) {
main.push(switchContainmentWarning);
categoriesScroll.enabled = false;
} else if (main.currentItem.objectName === "switchContainmentWarningItem") {
main.pop();
categoriesScroll.enabled = true;
}
}
}

View file

@ -0,0 +1,74 @@
/*
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2020 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.6
import org.kde.kirigami 2.5 as Kirigami
Kirigami.Page {
id: container
required property QtObject kcm
required property Item internalPage
signal settingValueChanged()
title: kcm.name
topPadding: 0
leftPadding: 0
rightPadding: 0
bottomPadding: 0
flickable: internalPage.flickable
actions: internalPage.actions
onInternalPageChanged: {
internalPage.parent = contentItem;
internalPage.anchors.fill = contentItem;
}
onActiveFocusChanged: {
if (activeFocus) {
internalPage.forceActiveFocus();
}
}
Component.onCompleted: {
kcm.load()
}
function saveConfig() {
kcm.save();
}
data: [
Connections {
target: kcm
onPagePushed: {
app.pageStack.push(configurationKcmPageComponent.createObject(app.pageStack, {"kcm": kcm, "internalPage": page}));
}
onPageRemoved: app.pageStack.pop();
},
Connections {
target: app.pageStack
onPageRemoved: {
if (kcm.needsSave) {
kcm.save()
}
if (page == container) {
page.destroy();
}
}
}
]
Connections {
target: kcm
function onNeedsSaveChanged() {
if (kcm.needsSave) {
container.settingValueChanged()
}
}
}
}

View file

@ -0,0 +1,46 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.0
import QtQuick.Controls 2.3 as QQC2
import QtQuick.Layouts 1.0
import org.kde.kquickcontrols 2.0
import org.kde.kirigami 2.14 as Kirigami
import org.kde.plasma.plasmoid 2.0
import org.kde.kcmutils as KCM
KCM.SimpleKCM {
id: root
title: i18n("Shortcuts")
signal configurationChanged
function saveConfig() {
Plasmoid.globalShortcut = button.keySequence
}
ColumnLayout {
spacing: Kirigami.Units.smallSpacing
QQC2.Label {
Layout.fillWidth: true
text: i18nd("plasma_shell_org.kde.plasma.desktop", "This shortcut will activate the applet as though it had been clicked.")
textFormat: Text.PlainText
wrapMode: Text.WordWrap
}
KeySequenceItem {
id: button
keySequence: Plasmoid.globalShortcut
modifierOnlyAllowed: true
onCaptureFinished: {
if (keySequence !== Plasmoid.globalShortcut) {
root.configurationChanged();
}
}
}
}
}

View file

@ -0,0 +1,40 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.0
import org.kde.kirigami 2.15 as Kirigami
import org.kde.plasma.configuration 2.0
import org.kde.plasma.plasmoid 2.0
AppletConfiguration {
id: root
isContainment: true
Layout.minimumWidth: Kirigami.Units.gridUnit * 35
Layout.minimumHeight: Kirigami.Units.gridUnit * 30
Layout.preferredWidth: Kirigami.Units.gridUnit * 32
Layout.preferredHeight: Kirigami.Units.gridUnit * 36
//BEGIN model
globalConfigModel: globalContainmentConfigModel
ConfigModel {
id: globalContainmentConfigModel
ConfigCategory {
name: i18nd("plasma_shell_org.kde.plasma.desktop", "Wallpaper")
icon: "preferences-desktop-wallpaper"
source: "ConfigurationContainmentAppearance.qml"
includeMargins: false
}
ConfigCategory {
name: i18nd("plasma_shell_org.kde.plasma.desktop", "Mouse Actions")
icon: "preferences-desktop-mouse"
source: "ConfigurationContainmentActions.qml"
}
}
//END model
}

View file

@ -0,0 +1,53 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.0
import QtQuick.Controls 2.3 as QQC2
QQC2.Button {
id: mouseInputButton
property string defaultText: i18nd("plasma_shell_org.kde.plasma.desktop", "Add Action")
text: defaultText
checkable: true
property string eventString
onCheckedChanged: {
if (checked) {
text = i18nd("plasma_shell_org.kde.plasma.desktop", "Input Here");
mouseInputArea.enabled = true;
}
}
MouseArea {
id: mouseInputArea
anchors.fill: parent
acceptedButtons: Qt.AllButtons
enabled: false
onClicked: {
var newEventString = configDialog.currentContainmentActionsModel.mouseEventString(mouse.button, mouse.modifiers);
if (eventString === newEventString || !configDialog.currentContainmentActionsModel.isTriggerUsed(newEventString)) {
eventString = newEventString;
mouseInputButton.text = defaultText;
mouseInputButton.checked = false;
enabled = false;
}
}
onWheel: {
var newEventString = configDialog.currentContainmentActionsModel.wheelEventString(wheel);
if (eventString === newEventString || !configDialog.currentContainmentActionsModel.isTriggerUsed(newEventString)) {
eventString = newEventString;
mouseInputButton.text = defaultText;
mouseInputButton.checked = false;
enabled = false;
}
}
}
}

View file

@ -0,0 +1,632 @@
/*
SPDX-FileCopyrightText: 2023 Niccolò Venerandi <niccolo.venerandi@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls 2.4 as QQC2
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kirigami 2.20 as Kirigami
import org.kde.ksvg 1.0 as KSvg
import org.kde.plasma.components 3.0 as PC3
import org.kde.plasma.shell.panel 0.1 as Panel
import org.kde.kquickcontrols 2.0
import "panelconfiguration"
ColumnLayout {
id: dialogRoot
spacing: Kirigami.Units.largeSpacing * 2
signal closeContextMenu
required property QtObject panelConfiguration
property bool vertical: (panel.location === PlasmaCore.Types.LeftEdge || panel.location === PlasmaCore.Types.RightEdge)
readonly property int headingLevel: 2
property Item panelRuler: Ruler {
id: ruler
prefix: {
switch (panel.location) {
case PlasmaCore.Types.TopEdge:
return "north"
case PlasmaCore.Types.LeftEdge:
return "west"
case PlasmaCore.Types.RightEdge:
return "east"
case PlasmaCore.Types.BottomEdge:
default:
return "south"
}
}
Item {
activeFocusOnTab: true
onActiveFocusChanged: {
if (activeFocus && dialogRoot.Window.window && dialogRoot.Window.window.visible) {
dialogRoot.Window.window.requestActivate()
}
}
}
// This item is used to "pass" focus to the main window when we're at the last of the control of the ruler
Item {
parent: dialogRoot.parent // Used to not take space in the ColumnLayout
activeFocusOnTab: true
onActiveFocusChanged: {
let window = dialogRoot.Window.window
if (activeFocus && window && window.visible) {
window.requestActivate()
}
}
}
}
Connections {
target: panel
function onOffsetChanged() {
ruler.offset = panel.offset
}
function onMinimumLengthChanged() {
ruler.minimumLength = panel.minimumLength
}
function onMaximumLengthChanged() {
ruler.maximumLength = panel.maximumLength
}
}
Component.onCompleted: {
if (panel.lengthMode === Panel.Global.Custom) {
Qt.callLater(()=> {
panelConfiguration.panelRulerView.visible = true
})
}
}
PlasmaExtras.PlasmoidHeading {
RowLayout {
anchors.fill: parent
spacing: Kirigami.Units.largeSpacing
Kirigami.Heading {
Layout.leftMargin: Kirigami.Units.smallSpacing
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Panel Settings")
textFormat: Text.PlainText
}
Item { Layout.fillWidth: true }
PC3.ToolButton {
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Add Spacer")
icon.name: "distribute-horizontal-x"
PC3.ToolTip.text: i18nd("plasma_shell_org.kde.plasma.desktop", "Add spacer widget to the panel")
PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
PC3.ToolTip.visible: hovered
onClicked: configDialog.addPanelSpacer()
}
PC3.ToolButton {
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Add Widgets…")
icon.name: "list-add"
PC3.ToolTip.text: i18nd("plasma_shell_org.kde.plasma.desktop", "Open the widget selector to drag and drop widgets to the panel")
PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
PC3.ToolTip.visible: hovered
onClicked: {
configDialog.close()
configDialog.showAddWidgetDialog()
}
}
}
}
GridLayout {
Layout.leftMargin: columnSpacing
Layout.rightMargin: columnSpacing
Layout.alignment: Qt.AlignHCenter
Layout.minimumWidth: (positionRepresentation.implicitWidth + columnSpacing) * columns + columnSpacing
rowSpacing: dialogRoot.spacing
columnSpacing: Kirigami.Units.smallSpacing
rows: 2
columns: 3
uniformCellWidths: true
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Kirigami.Units.mediumSpacing
Kirigami.Heading {
Layout.alignment: Qt.AlignHCenter
level: dialogRoot.headingLevel
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Position")
textFormat: Text.PlainText
}
PanelRepresentation {
id: positionRepresentation
text: (panel.location === PlasmaCore.Types.TopEdge ? i18nd("plasma_shell_org.kde.plasma.desktop", "Top") :
panel.location === PlasmaCore.Types.RightEdge ? i18nd("plasma_shell_org.kde.plasma.desktop", "Right") :
panel.location === PlasmaCore.Types.LeftEdge ? i18nd("plasma_shell_org.kde.plasma.desktop", "Left") :
i18nd("plasma_shell_org.kde.plasma.desktop", "Bottom"))
Layout.alignment: Qt.AlignHCenter
alignment: (panel.location === PlasmaCore.Types.TopEdge ? Qt.AlignHCenter | Qt.AlignTop :
panel.location === PlasmaCore.Types.RightEdge ? Qt.AlignVCenter | Qt.AlignRight :
panel.location === PlasmaCore.Types.LeftEdge ? Qt.AlignVCenter | Qt.AlignLeft :
Qt.AlignHCenter | Qt.AlignBottom)
isVertical: dialogRoot.vertical
mainIconSource: (panel.location === PlasmaCore.Types.TopEdge ? "arrow-up" :
panel.location === PlasmaCore.Types.RightEdge ? "arrow-right" :
panel.location === PlasmaCore.Types.LeftEdge ? "arrow-left": "arrow-down")
onClicked: {
setPositionButton.checked = !setPositionButton.checked
setPositionButton.forceActiveFocus()
}
}
PC3.Button {
id: setPositionButton
Layout.minimumHeight: transparencyBox.height
Layout.minimumWidth: positionRepresentation.width
Layout.alignment: Qt.AlignHCenter
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Set Position…")
checkable: true
function moveTo(newLocation: int, associatedWindow = null) {
if (!setPositionButton.checked) {
return;
}
panel.location = newLocation;
if (associatedWindow !== null) {
panel.screenToFollow = dialogRoot.panelConfiguration.screenFromWindow(associatedWindow);
}
setPositionButton.checked = false;
}
Keys.onLeftPressed: moveTo(PlasmaCore.Types.LeftEdge)
Keys.onRightPressed: moveTo(PlasmaCore.Types.RightEdge)
Keys.onUpPressed: moveTo(PlasmaCore.Types.TopEdge)
Keys.onDownPressed: moveTo(PlasmaCore.Types.BottomEdge)
}
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Kirigami.Units.mediumSpacing
Kirigami.Heading {
Layout.alignment: Qt.AlignHCenter
level: dialogRoot.headingLevel
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Alignment")
textFormat: Text.PlainText
}
PanelRepresentation {
id: alignmentRepresentation
Layout.alignment: Qt.AlignHCenter
mainIconSource: {
if (dialogRoot.vertical) {
if (alignmentBox.previewIndex === 0) {
return "align-vertical-top"
} else if (alignmentBox.previewIndex === 1) {
return "align-vertical-center"
} else {
return "align-vertical-bottom"
}
} else {
if (alignmentBox.previewIndex === 0) {
return "align-horizontal-left"
} else if (alignmentBox.previewIndex === 1) {
return "align-horizontal-center"
} else {
return "align-horizontal-right"
}
}
}
alignment: {
let first, second;
if (dialogRoot.vertical) {
if (alignmentBox.previewIndex === 0) {
first = Qt.AlignTop
} else if (alignmentBox.previewIndex === 1) {
first = Qt.AlignVCenter
} else {
first = Qt.AlignBottom
}
if (panel.location === PlasmaCore.Types.LeftEdge) {
second = Qt.AlignLeft
} else {
second = Qt.AlignRight
}
} else {
if (alignmentBox.previewIndex === 0) {
first = Qt.AlignLeft
} else if (alignmentBox.previewIndex === 1) {
first = Qt.AlignHCenter
} else {
first = Qt.AlignRight
}
if (panel.location === PlasmaCore.Types.TopEdge) {
second = Qt.AlignTop
} else {
second = Qt.AlignBottom
}
}
return first | second;
}
onClicked: alignmentBox.popup.visible = true
isVertical: dialogRoot.vertical
}
PC3.ComboBox {
id: alignmentBox
property int previewIndex: highlightedIndex > -1 ? highlightedIndex : currentIndex
Layout.alignment: Qt.AlignHCenter
Layout.minimumWidth: alignmentRepresentation.width
model: [
dialogRoot.vertical ? i18nd("plasma_shell_org.kde.plasma.desktop", "Top") : i18nd("plasma_shell_org.kde.plasma.desktop", "Left"),
i18nd("plasma_shell_org.kde.plasma.desktop", "Center"),
dialogRoot.vertical ? i18nd("plasma_shell_org.kde.plasma.desktop", "Bottom") : i18nd("plasma_shell_org.kde.plasma.desktop", "Right")
]
currentIndex: (panel.alignment === Qt.AlignLeft ? 0 :
panel.alignment === Qt.AlignCenter ? 1 : 2)
onActivated: (index) => {
if (index === 0) {
panel.alignment = Qt.AlignLeft
} else if (index === 1) {
panel.alignment = Qt.AlignCenter
} else {
panel.alignment = Qt.AlignRight
}
}
}
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Kirigami.Units.mediumSpacing
Kirigami.Heading {
level: dialogRoot.headingLevel
Layout.alignment: Qt.AlignHCenter
text: dialogRoot.vertical ? i18nd("plasma_shell_org.kde.plasma.desktop", "Height")
: i18nd("plasma_shell_org.kde.plasma.desktop", "Width")
textFormat: Text.PlainText
}
PanelRepresentation {
id: lengthRepresentation
Layout.alignment: Qt.AlignHCenter
mainIconSource: (widthBox.previewIndex === 1 ? "gnumeric-ungroup" :
widthBox.previewIndex === 0 ? (dialogRoot.vertical ? "panel-fit-height" : "panel-fit-width") : "kdenlive-custom-effect")
isVertical: dialogRoot.vertical
alignment: positionRepresentation.alignment
fillAvailable: widthBox.previewIndex === 0
onClicked: widthBox.popup.visible = true
}
PC3.ComboBox {
id: widthBox
property int previewIndex: highlightedIndex > -1 ? highlightedIndex : currentIndex
Layout.alignment: Qt.AlignHCenter
Layout.minimumWidth: lengthRepresentation.width
model: [
dialogRoot.vertical ? i18nd("plasma_shell_org.kde.plasma.desktop", "Fill height") : i18nd("plasma_shell_org.kde.plasma.desktop", "Fill width"),
i18nd("plasma_shell_org.kde.plasma.desktop", "Fit content"),
i18nd("plasma_shell_org.kde.plasma.desktop", "Custom")
]
currentIndex: (panel.lengthMode === Panel.Global.FillAvailable ? 0 :
panel.lengthMode === Panel.Global.FitContent ? 1 : 2)
onActivated: (index) => {
if (index === 0) {
panel.lengthMode = Panel.Global.FillAvailable
panelConfiguration.panelRulerView.visible = false
} else if (index === 1) {
panel.lengthMode = Panel.Global.FitContent
panelConfiguration.panelRulerView.visible = false
} else {
panel.lengthMode = Panel.Global.Custom
panelConfiguration.panelRulerView.visible = true
}
}
}
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Kirigami.Units.mediumSpacing
Kirigami.Heading {
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
level: dialogRoot.headingLevel
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Visibility")
textFormat: Text.PlainText
}
PanelRepresentation {
id: visibilityRepresentation
Layout.alignment: Qt.AlignHCenter
sunkenPanel: autoHideBox.previewIndex !== 0
onClicked: autoHideBox.popup.visible = true
}
PC3.ComboBox {
id: autoHideBox
property int previewIndex: popup.visible ? highlightedIndex : currentIndex
model: [
i18nd("plasma_shell_org.kde.plasma.desktop", "Always visible"),
i18nd("plasma_shell_org.kde.plasma.desktop", "Auto hide"),
i18nd("plasma_shell_org.kde.plasma.desktop", "Dodge windows"),
i18nd("plasma_shell_org.kde.plasma.desktop", "Windows Go Below"),
]
Layout.alignment: Qt.AlignHCenter
Layout.minimumWidth: visibilityRepresentation.width
currentIndex: {
switch (panel.visibilityMode) {
case Panel.Global.AutoHide:
return 1;
case Panel.Global.DodgeWindows:
return 2;
case Panel.Global.WindowsGoBelow:
return 3;
case Panel.Global.NormalPanel:
default:
return 0;
}
}
onActivated: (index) => {
switch (index) {
case 1:
panel.visibilityMode = Panel.Global.AutoHide;
break;
case 2:
panel.visibilityMode = Panel.Global.DodgeWindows;
break;
case 3:
panel.visibilityMode = Panel.Global.WindowsGoBelow;
break;
case 0:
default:
panel.visibilityMode = Panel.Global.NormalPanel;
break;
}
}
}
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Kirigami.Units.mediumSpacing
Kirigami.Heading {
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
level: dialogRoot.headingLevel
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Opacity")
textFormat: Text.PlainText
}
PanelRepresentation {
id: opacityRepresentation
Layout.alignment: Qt.AlignHCenter
adaptivePanel: transparencyBox.previewIndex === 0
translucentPanel: transparencyBox.previewIndex === 2
onClicked: transparencyBox.popup.visible = true
}
PC3.ComboBox {
id: transparencyBox
readonly property int previewIndex: popup.visible ? highlightedIndex : currentIndex
model: [
i18nd("plasma_shell_org.kde.plasma.desktop", "Adaptive"),
i18nd("plasma_shell_org.kde.plasma.desktop", "Opaque"),
i18nd("plasma_shell_org.kde.plasma.desktop", "Translucent")
]
Layout.alignment: Qt.AlignHCenter
Layout.minimumWidth: opacityRepresentation.width
currentIndex: (panel.opacityMode === Panel.Global.Adaptive ? 0 :
panel.opacityMode === Panel.Global.Opaque ? 1 : 2)
onActivated: (index) => {
if (index === 0) {
panel.opacityMode = Panel.Global.Adaptive
} else if (index === 1) {
panel.opacityMode = Panel.Global.Opaque
} else {
panel.opacityMode = Panel.Global.Translucent
}
}
}
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Kirigami.Units.mediumSpacing
Kirigami.Action {
id: floatingAction
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Floating")
checkable: true
checked: panel.floating
onToggled: source => {
panel.floating = checked;
}
}
Kirigami.Heading {
level: dialogRoot.headingLevel
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Style")
textFormat: Text.PlainText
}
PanelRepresentation {
Layout.alignment: Qt.AlignHCenter
floatingGap: Kirigami.Units.smallSpacing * floatingSwitch.checked
onClicked: floatingAction.toggle(this)
}
PC3.Switch {
id: floatingSwitch
Layout.alignment: Qt.AlignHCenter
Layout.minimumHeight: transparencyBox.height
action: floatingAction
}
}
}
Instantiator {
active: setPositionButton.checked
asynchronous: true
model: Application.screens
Item {
width: 0
height: 0
required property var modelData
component Indicator : PlasmaCore.Dialog {
id: root
property string iconSource
property var onClickedLocation
flags: Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus | Qt.BypassWindowManagerHint
location: PlasmaCore.Types.Floating
visible: setPositionButton.checked && (panel.location !== onClickedLocation || modelData.name !== panel.screenToFollow.name)
x: modelData.virtualX + Kirigami.Units.largeSpacing
y: modelData.virtualY + modelData.height / 2 - mainItem.height / 2 - margins.top
mainItem: PC3.ToolButton {
width: Kirigami.Units.iconSizes.enormous
height: Kirigami.Units.iconSizes.enormous
icon.name: root.iconSource
onClicked: setPositionButton.moveTo(root.onClickedLocation, Window.window)
}
}
Indicator {
x: modelData.virtualX + Kirigami.Units.largeSpacing
y: modelData.virtualY + modelData.height / 2 - mainItem.height / 2 - margins.top
iconSource: "arrow-left"
onClickedLocation: PlasmaCore.Types.LeftEdge
}
Indicator {
x: modelData.virtualX + modelData.width - Kirigami.Units.largeSpacing - margins.left - margins.right - mainItem.width
y: modelData.virtualY + modelData.height / 2 - mainItem.height / 2 - margins.top
iconSource: "arrow-right"
onClickedLocation: PlasmaCore.Types.RightEdge
}
Indicator {
x: modelData.virtualX + modelData.width / 2 - mainItem.width / 2 - margins.left
y: modelData.virtualY + Kirigami.Units.largeSpacing
iconSource: "arrow-up"
onClickedLocation: PlasmaCore.Types.TopEdge
}
Indicator {
x: modelData.virtualX + modelData.width / 2 - mainItem.width / 2 - margins.left
y: modelData.virtualY + modelData.height - mainItem.height - margins.top - margins.bottom - Kirigami.Units.largeSpacing
iconSource: "arrow-down"
onClickedLocation: PlasmaCore.Types.BottomEdge
}
}
}
GridLayout {
Layout.alignment: Qt.AlignHCenter
rowSpacing: Kirigami.Units.largeSpacing
columnSpacing: Kirigami.Units.largeSpacing
rows: 2
columns: 2
PC3.Label {
id: spinBoxLabel
Layout.alignment: Qt.AlignRight
wrapMode: Text.Wrap
text: panel.location === PlasmaCore.Types.LeftEdge || panel.location === PlasmaCore.Types.RightEdge
? i18nd("plasma_shell_org.kde.plasma.desktop", "Panel Width:")
: i18nd("plasma_shell_org.kde.plasma.desktop", "Panel Height:")
textFormat: Text.PlainText
}
PC3.SpinBox {
id: spinBox
editable: true
focus: !Kirigami.InputMethod.willShowOnActive
from: Math.max(20, panel.minThickness) // below this size, the panel is mostly unusable
to: panel.location === PlasmaCore.Types.LeftEdge || panel.location === PlasmaCore.Types.RightEdge
? panel.screenToFollow.geometry.width / 2
: panel.screenToFollow.geometry.height / 2
stepSize: 2
value: panel.thickness
onValueModified: {
panel.thickness = value
}
}
PC3.Label {
Layout.alignment: Qt.AlignRight
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Focus shortcut:")
textFormat: Text.PlainText
visible: panel.adaptiveOpacityEnabled
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
}
PC3.ToolTip {
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Press this keyboard shortcut to move focus to the Panel")
visible: mouseArea.containsMouse
}
}
KeySequenceItem {
id: button
keySequence: plasmoid.globalShortcut
onCaptureFinished: {
plasmoid.globalShortcut = button.keySequence
}
}
}
PlasmaExtras.PlasmoidHeading {
position: PlasmaExtras.PlasmoidHeading.Footer
Layout.topMargin: Kirigami.Units.smallSpacing
topPadding: Kirigami.Units.smallSpacing * 2
leftPadding: Kirigami.Units.smallSpacing
rightPadding: Kirigami.Units.smallSpacing
bottomPadding: Kirigami.Units.smallSpacing
Layout.fillWidth: true
RowLayout {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
spacing: Kirigami.Units.largeSpacing
PC3.ToolButton {
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Delete the panel", "Delete Panel")
icon.name: "delete"
PC3.ToolTip.text: i18nd("plasma_shell_org.kde.plasma.desktop", "Remove this panel; this action is undo-able")
PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
PC3.ToolTip.visible: hovered
onClicked: plasmoid.internalAction("remove").trigger()
}
Item {Layout.fillWidth: true}
PC3.ToolButton {
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Done configuring the panel", "Done")
icon.name: "dialog-ok-symbolic"
PC3.ToolTip.text: i18nd("plasma_shell_org.kde.plasma.desktop", "Close Panel Settings window and exit Edit Mode")
PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
PC3.ToolTip.visible: hovered
onClicked: plasmoid.containment.corona.editMode = false
}
}
}
// This item is used to "pass" focus to the ruler with tab when we're at the last of the control of this window
Item {
parent: dialogRoot.parent // Used to not take space in the ColumnLayout
activeFocusOnTab: true
onActiveFocusChanged: {
let window = ruler.Window.window
if (activeFocus && window && window.visible) {
window.requestActivate()
}
}
}
}

View file

@ -0,0 +1,85 @@
/*
SPDX-FileCopyrightText: 2021 Cyril Rossi <cyril.rossi@enioka.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.4 as QQC2
import QtQuick.Layouts 1.3
import QtQuick.Window 2.15
import org.kde.kirigami 2.13 as Kirigami
import "shellcontainmentconfiguration"
Kirigami.AbstractApplicationWindow {
id: root
title: i18nd("plasma_shell_org.kde.plasma.desktop", "Panel and Desktop Management")
width: Kirigami.Units.gridUnit * 40
height: Kirigami.Units.gridUnit * 32
minimumWidth: Kirigami.Units.gridUnit * 30
minimumHeight: Kirigami.Units.gridUnit * 25
header: QQC2.ToolBar {
anchors {
left: parent.left
right: parent.right
}
contentItem: QQC2.Label {
Layout.fillWidth: parent
text: i18nd("plasma_shell_org.kde.plasma.desktop", "You can drag Panels and Desktops around to move them to different screens.")
textFormat: Text.PlainText
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
}
}
footer: QQC2.Control {
contentItem: QQC2.DialogButtonBox {
QQC2.Button {
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Close")
onClicked: Window.window.close()
}
}
background: Item {
// FIXME: automate that somehow?
Kirigami.Separator {
anchors {
left: parent.left
top: parent.top
right: parent.right
}
visible: mainPage.flickable.contentHeight > mainPage.flickable.height
}
}
}
Kirigami.ScrollablePage {
id: mainPage
anchors.fill: parent
leftPadding: 0
topPadding: 0
rightPadding: 0
bottomPadding: 0
Flow {
id: mainGrid
width: mainPage.flickable.width
spacing: 0
Repeater {
id: repeater
model: ShellContainmentModel
delegate: Delegate {
viewPort: mainPage
}
}
}
}
}

View file

@ -0,0 +1,174 @@
import QtQuick
import QtQuick.Layouts
import org.kde.plasma.components as PC3
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.extras as PlasmaExtras
import org.kde.ksvg as KSvg
import org.kde.plasma.shell.panel as Panel
import org.kde.kirigami as Kirigami
Item {
id: root
property string text
property /*Qt::Alignment*/int alignment: Qt.AlignHCenter | Qt.AlignBottom
property string tooltip
property bool isVertical: false
property bool checked: false
property bool windowVisible: false
property bool panelVisible: true
property bool translucentPanel: false
property bool sunkenPanel: false
property bool adaptivePanel: false
property bool fillAvailable: false
property int floatingGap: 0
property int windowZ: 0
property var mainIconSource: null
property int screenHeight: Math.round(screenRect.height / 2)
readonly property bool iconAndLabelsShouldlookSelected: checked || mouseArea.pressed
signal clicked()
implicitHeight: mainItem.height
implicitWidth: mainItem.width
PC3.ToolTip {
text: root.tooltip
visible: mouseArea.containsMouse && text.length > 0
}
PlasmaExtras.Highlight {
anchors.fill: parent
anchors.margins: -Kirigami.Units.smallSpacing
hovered: mouseArea.containsMouse
pressed: root.iconAndLabelsShouldlookSelected
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: root.clicked()
}
ColumnLayout {
id: mainItem
spacing: Kirigami.Units.smallSpacing
Rectangle {
id: screenRect
Layout.alignment: Qt.AlignHCenter
implicitWidth: Math.round(Math.min(Kirigami.Units.gridUnit * 6, Screen.width * 0.1))
implicitHeight: Math.round(Math.min(Kirigami.Units.gridUnit * 4, Screen.width * 0.1))
color: Qt.tint(Kirigami.Theme.backgroundColor, Qt.rgba(1, 1, 1, 0.3))
border.color: Kirigami.Theme.highlightColor
radius: Kirigami.Units.cornerRadius
clip: root.sunkenPanel
RowLayout {
anchors.fill: parent
Rectangle {
id: panelImage
implicitWidth: root.isVertical ? Math.round(parent.width / 6) : Math.round(parent.width * (root.fillAvailable ? 1 : 0.8))
implicitHeight: root.isVertical ? Math.round(parent.height * (root.fillAvailable ? 1 : 0.8)) : Math.round(parent.height / 4)
Layout.alignment: root.alignment
Layout.bottomMargin: root.sunkenPanel * -Math.round(height / 2) + root.floatingGap
color: root.translucentPanel ? screenRect.color : Kirigami.Theme.backgroundColor
opacity: root.translucentPanel ? 0.8 : 1.0
border.color: "transparent"
visible: root.panelVisible
clip: root.adaptivePanel
radius: Kirigami.Units.cornerRadius
z: 1
Loader {
id: horizontalAdaptivePanelLoader
active: root.adaptivePanel && !root.isVertical
sourceComponent: Rectangle {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: Math.round(panelImage.width / 3)
color: Qt.lighter(screenRect.color)
border.color: Kirigami.Theme.highlightColor
width: panelImage.width
height: Math.round(panelImage.height * 4)
radius: Math.round(height / 2)
rotation: 45
}
}
Loader {
id: verticalAdaptivePanelLoader
active: root.adaptivePanel && root.isVertical
sourceComponent: Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Math.round(panelImage.height / 4)
color: Qt.lighter(screenRect.color)
border.color: Kirigami.Theme.highlightColor
width: Math.round(panelImage.width * 2)
height: panelImage.height
radius: Math.round(height / 2)
rotation: 45
}
}
Rectangle {
id: panelBorder
anchors.fill: parent
color: "transparent"
border.color: Kirigami.Theme.highlightColor
radius: panelImage.radius
}
}
}
Rectangle {
id: window
width: Math.round(parent.width / 2)
height: Math.round(parent.height / 2)
visible: root.windowVisible
radius: 5
color: Kirigami.Theme.highlightColor
border.color: "transparent"
x: root.isVertical ? Math.round(panelImage.x + panelImage.width / 2) : Math.round(screenRect.width / 2 - width / 2) + Kirigami.Units.gridUnit
y: root.isVertical ? Math.round(screenRect.height / 2 - height / 2) : Math.round(panelImage.y - height + panelImage.height / 2)
z: root.windowZ
Row {
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: Kirigami.Units.smallSpacing
spacing: Kirigami.Units.smallSpacing
Repeater {
model: 3
delegate: Rectangle {
width: Math.round(Kirigami.Units.gridUnit / 6)
height: width
radius: Math.round(height / 2)
color: Kirigami.Theme.textColor
}
}
}
}
Kirigami.Icon {
id: mainIcon
visible: valid
anchors.centerIn: parent
transform: Translate {
y: root.isVertical ? 0 : Math.round((mainIcon.y - panelImage.y) / 4)
x: root.isVertical ? Math.round((mainIcon.x - panelImage.x) / 4) : 0
}
height: parent.height / 2
source: root.mainIconSource
}
}
}
}

View file

@ -0,0 +1,241 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.4 as QQC2
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kirigami 2.20 as Kirigami
import org.kde.ksvg 1.0 as KSvg
import org.kde.plasma.components 3.0 as PC3
import org.kde.plasma.shell.panel 0.1 as Panel
import org.kde.kquickcontrols 2.0
KSvg.FrameSvgItem {
id: root
anchors.fill: parent
//Those properties get updated by PanelConfiguration.qml whenever a value in the panel changes
property alias offset: offsetHandle.value
property alias minimumLength: rightMinimumLengthHandle.value
property alias maximumLength: rightMaximumLengthHandle.value
property bool isHorizontal: root.prefix[0] === 'north' || root.prefix[0] === 'south'
property string maximumText: (dialogRoot.vertical ? i18nd("plasma_shell_org.kde.plasma.desktop", "Drag to change maximum height.") : i18nd("plasma_shell_org.kde.plasma.desktop", "Drag to change maximum width.")) + "\n" + i18nd("plasma_shell_org.kde.plasma.desktop", "Double click to reset.")
property string minimumText: (dialogRoot.vertical ? i18nd("plasma_shell_org.kde.plasma.desktop", "Drag to change minimum height.") : i18nd("plasma_shell_org.kde.plasma.desktop", "Drag to change minimum width.")) + "\n" + i18nd("plasma_shell_org.kde.plasma.desktop", "Double click to reset.")
imagePath: "widgets/containment-controls"
implicitWidth: Math.max(offsetHandle.width, rightMinimumLengthHandle.width + rightMaximumLengthHandle.width)
implicitHeight: Math.max(offsetHandle.height, rightMinimumLengthHandle.height + rightMaximumLengthHandle.height)
onMinimumLengthChanged: rightMinimumLengthHandle.value = leftMinimumLengthHandle.value = minimumLength
onMaximumLengthChanged: rightMaximumLengthHandle.value = leftMaximumLengthHandle.value = maximumLength
/* As offset and length have a different meaning in all alignments, the panel shifts on alignment change.
* This could result in wrong panel positions (e.g. panel shifted over monitor border).
* The fancy version would be a recalculation of all values, so that the panel stays at it's current position,
* but this would be error prone and complicated. As the panel alignment is rarely changed, it's not worth it.
* The more easy approach is just setting the panel offset to zero. This makes sure the panel has a valid position and size.
*/
Connections {
target: panel
function onAlignmentChanged() {
offset = 0
}
}
Component.onCompleted: {
offsetHandle.value = panel.offset
rightMinimumLengthHandle.value = panel.minimumLength
rightMaximumLengthHandle.value = panel.maximumLength
leftMinimumLengthHandle.value = panel.minimumLength
leftMaximumLengthHandle.value = panel.maximumLength
}
KSvg.SvgItem {
id: centerMark
imagePath: "widgets/containment-controls"
elementId: dialogRoot.vertical ? "vertical-centerindicator" : "horizontal-centerindicator"
visible: panel.alignment === Qt.AlignCenter
width: dialogRoot.vertical ? parent.width : naturalSize.width
height: dialogRoot.vertical ? naturalSize.height : parent.height
anchors.centerIn: parent
}
SliderHandle {
id: offsetHandle
anchors {
right: !root.isHorizontal ? root.right : undefined
bottom: root.isHorizontal ? root.bottom : undefined
}
graphicElementName: "offsetslider"
description: i18nd("plasma_shell_org.kde.plasma.desktop", "Drag to change position on this screen edge.\nDouble click to reset.")
offset: panel.alignment === Qt.AlignCenter ? 0 : (dialogRoot.vertical ? panel.height : panel.width) / 2
property int position: (dialogRoot.vertical) ? y + height / 2 : x + width / 2
onPositionChanged: {
if (!offsetHandle.hasEverBeenMoved) return;
let panelLength = dialogRoot.vertical ? panel.height : panel.width
let rootLength = dialogRoot.vertical ? root.height : root.width
// Snap at the center
if (Math.abs(position - rootLength / 2) < 5) {
if (panel.alignment !== Qt.AlignCenter) {
panel.alignment = Qt.AlignCenter
// Coordinate change: since we switch from measuring the min/max
// length from the side of the panel to the center of the panel,
// we need to double the distance between the min/max indicators
// and the panel side.
panel.minimumLength += panel.minimumLength - panelLength
panel.maximumLength += panel.maximumLength - panelLength
}
panel.offset = 0
} else if (position > rootLength / 2) {
if (panel.alignment === Qt.AlignCenter) {
// This is the opposite of the previous comment, as we are
// cutting in half the distance between the min/max indicators
// and the side of the panel.
panel.minimumLength -= (panel.minimumLength - panelLength) / 2
panel.maximumLength -= (panel.maximumLength - panelLength) / 2
}
panel.alignment = Qt.AlignRight
panel.offset = Math.round(rootLength - position - offset)
} else if (position <= rootLength / 2) {
if (panel.alignment === Qt.AlignCenter) {
panel.minimumLength -= (panel.minimumLength - panelLength) / 2
panel.maximumLength -= (panel.maximumLength - panelLength) / 2
}
panel.alignment = Qt.AlignLeft
panel.offset = Math.round(position - offset)
}
}
/* The maximum/minimumPosition values are needed to prevent the user from moving a panel with
* center alignment to the left and then drag the position handle to the left.
* This would make the panel to go off the monitor:
* |<- V -> |
* | -> | <- |
* ^move this slider to the left
*/
minimumPosition: {
var size = dialogRoot.vertical ? height : width
switch(panel.alignment){
case Qt.AlignLeft:
return -size / 2 + offset
case Qt.AlignRight:
return leftMaximumLengthHandle.value - size / 2 - offset
default:
return panel.maximumLength / 2 - size / 2
}
}
//Needed for the same reason as above
maximumPosition: {
var size = dialogRoot.vertical ? height : width
var rootSize = dialogRoot.vertical ? root.height : root.width
switch(panel.alignment){
case Qt.AlignLeft:
return rootSize - rightMaximumLengthHandle.value - size / 2 + offset
case Qt.AlignRight:
return rootSize - size / 2 - offset
default:
return rootSize - panel.maximumLength / 2 - size / 2
}
}
function defaultPosition(): int /*override*/ {
return 0;
}
}
/* The maximumPosition value for the right handles and the minimumPosition value for the left handles are
* needed to prevent the user from moving a panel with center alignment to the left (right) and then pull one of the
* right (left) sliders to the right (left).
* Because the left and right sliders are coupled, this would make the left (right) sliders to go off the monitor.
*
* |<- V -> |
* | -> | <- |
* ^move this slider to the right
*
* The other max/min Position values just set a minimum panel size
*/
SliderHandle {
id: rightMinimumLengthHandle
anchors {
left: !root.isHorizontal ? root.left : undefined
top: root.isHorizontal ? root.top : undefined
}
description: root.minimumText
alignment: panel.alignment | Qt.AlignLeft
visible: panel.alignment !== Qt.AlignRight
offset: panel.offset
graphicElementName: "minslider"
onValueChanged: panel.minimumLength = value
minimumPosition: offsetHandle.position + Kirigami.Units.gridUnit * 3
maximumPosition: {
var rootSize = dialogRoot.vertical ? root.height : root.width
var size = dialogRoot.vertical ? height : width
panel.alignment === Qt.AlignCenter ? Math.min(rootSize - size/2, rootSize + offset * 2 - size/2) : rootSize - size/2
}
}
SliderHandle {
id: rightMaximumLengthHandle
anchors {
right: !root.isHorizontal ? root.right : undefined
bottom: root.isHorizontal ? root.bottom : undefined
}
description: root.maximumText
alignment: panel.alignment | Qt.AlignLeft
visible: panel.alignment !== Qt.AlignRight
offset: panel.offset
graphicElementName: "maxslider"
onValueChanged: panel.maximumLength = value
minimumPosition: offsetHandle.position + Kirigami.Units.gridUnit * 3
maximumPosition: {
var rootSize = dialogRoot.vertical ? root.height : root.width
var size = dialogRoot.vertical ? height : width
panel.alignment === Qt.AlignCenter ? Math.min(rootSize - size/2, rootSize + offset * 2 - size/2) : rootSize - size/2
}
}
SliderHandle {
id: leftMinimumLengthHandle
anchors {
left: !root.isHorizontal ? root.left : undefined
top: root.isHorizontal ? root.top : undefined
}
description: root.minimumText
alignment: panel.alignment | Qt.AlignRight
visible: panel.alignment !== Qt.AlignLeft
offset: panel.offset
graphicElementName: "maxslider"
onValueChanged: panel.minimumLength = value
maximumPosition: offsetHandle.position - Kirigami.Units.gridUnit * 3
minimumPosition: {
var size = dialogRoot.vertical ? height : width
panel.alignment === Qt.AlignCenter ? Math.max(-size/2, offset*2 - size/2) : -size/2
}
}
SliderHandle {
id: leftMaximumLengthHandle
anchors {
right: !root.isHorizontal ? root.right : undefined
bottom: root.isHorizontal ? root.bottom : undefined
}
description: root.maximumText
alignment: panel.alignment | Qt.AlignRight
visible: panel.alignment !== Qt.AlignLeft
offset: panel.offset
graphicElementName: "minslider"
onValueChanged: panel.maximumLength = value
maximumPosition: offsetHandle.position - Kirigami.Units.gridUnit * 3
minimumPosition: {
var size = dialogRoot.vertical ? height : width
panel.alignment === Qt.AlignCenter ? Math.max(-size/2, offset*2 - size/2) : -size/2
}
}
}

View file

@ -0,0 +1,237 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.4 as QQC2
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kirigami 2.20 as Kirigami
import org.kde.ksvg 1.0 as KSvg
import org.kde.plasma.components 3.0 as PC3
import org.kde.plasma.shell.panel 0.1 as Panel
import org.kde.kquickcontrols 2.0
KSvg.SvgItem {
id: root
//Those properties get updated by PanelConfiguration.qml whenever a value changes
imagePath: "widgets/containment-controls"
elementId: parent.prefix + '-' + graphicElementName
width: naturalSize.width
height: naturalSize.height
//value expressed by this slider, this is the distance to offset
property int value
//name of the graphics to load
property string graphicElementName
//where the point "0" is
property int offset: 0
/*handle type: behave in different ways based on the alignment:
* alignment === Qt.AlignRight: Panel aligned to right and handle value relative to the right
* alignment === Qt.AlignLeft: Panel aligned to left and handle relative to the left
* (alignment !== Qt.AlignRight) && (alignment & Qt.AlignRight): Panel aligned to the center and handle right of offset and value doubled
* (alignment !== Qt.AlignLeft) && (alignment & Qt.AlignLeft): Panel aligned to the center and handle left of offset and value doubled
* else: Panel aligned to center and handle relative to the center
* Note that right/left and top/bottom are interchangeable
*/
property int alignment: panel.alignment
//The maximum/minimum Position (X/Y) the silder can be moved to
property int minimumPosition
property int maximumPosition
//Provide default position for "reset" action.
function defaultPosition(): int {
var dialogSize, panelSize;
if (dialogRoot.vertical) {
dialogSize = dialogRoot.height;
panelSize = panel.height;
} else {
dialogSize = dialogRoot.width;
panelSize = panel.width;
}
return (value === panelSize) ? dialogSize : panelSize;
}
// Handle name displayed as a tooltip.
property string description
property bool hasEverBeenMoved: false
function syncPos() {
if (dialogRoot.vertical) {
if (alignment === Qt.AlignRight) {
y = root.parent.height - (value + offset + root.height/2)
} else if (alignment === Qt.AlignLeft) {
y = value + offset - root.height/2
} else {
if (root.alignment & Qt.AlignRight) {
y = root.parent.height/2 - value/2 + offset - root.height/2
} else if (root.alignment & Qt.AlignLeft) {
y = root.parent.height/2 + value/2 + offset - root.height/2
} else {
y = root.parent.height/2 + value + offset -root.height/2
}
}
} else {
if (alignment === Qt.AlignRight) {
x = root.parent.width - (value + offset + root.width/2)
} else if (alignment === Qt.AlignLeft) {
x = value + offset - root.width/2
} else {
if (root.alignment & Qt.AlignRight) {
x = root.parent.width/2 - value/2 + offset - root.width/2
} else if (root.alignment & Qt.AlignLeft) {
x = root.parent.width/2 + value/2 + offset -root.width/2
} else {
x = root.parent.width/2 + value + offset -root.width/2
}
}
}
}
onValueChanged: syncPos()
onOffsetChanged: syncPos()
onAlignmentChanged: syncPos()
Connections {
target: root.parent
function onWidthChanged() {
syncPos()
}
function onHeightChanged() {
syncPos()
}
}
PC3.ToolTip {
text: root.description
visible: root.description !== "" && ((area.containsMouse && !area.containsPress) || area.activeFocus)
}
MouseArea {
id: area
drag {
target: parent
axis: (dialogRoot.vertical) ? Drag.YAxis : Drag.XAxis
minimumX: root.minimumPosition
minimumY: root.minimumPosition
maximumX: root.maximumPosition
maximumY: root.maximumPosition
}
anchors {
fill: parent
leftMargin: (dialogRoot.vertical) ? 0 : -Kirigami.Units.gridUnit
rightMargin: (dialogRoot.vertical) ? 0 : -Kirigami.Units.gridUnit
topMargin: (dialogRoot.vertical) ? -Kirigami.Units.gridUnit : 0
bottomMargin: (dialogRoot.vertical) ? -Kirigami.Units.gridUnit : 0
}
readonly property int keyboardMoveStepSize: Math.ceil((root.maximumPosition - root.minimumPosition) / 20)
activeFocusOnTab: true
hoverEnabled: true
cursorShape: dialogRoot.vertical ? Qt.SizeVerCursor : Qt.SizeHorCursor
Accessible.description: root.description
Keys.onEnterPressed: doubleClicked(null);
Keys.onReturnPressed: doubleClicked(null);
Keys.onSpacePressed: doubleClicked(null);
// BEGIN Arrow keys
Keys.onUpPressed: if (dialogRoot.vertical) {
root.y = Math.max(root.minimumPosition, root.y - ((event.modifiers & Qt.ShiftModifier) ? 1 : keyboardMoveStepSize));
changePosition();
} else {
event.accepted = false;
}
Keys.onDownPressed: if (dialogRoot.vertical) {
root.y = Math.min(root.maximumPosition, root.y + ((event.modifiers & Qt.ShiftModifier) ? 1 : keyboardMoveStepSize));
changePosition();
} else {
event.accepted = false;
}
Keys.onLeftPressed: if (!dialogRoot.vertical) {
root.x = Math.max(root.minimumPosition, root.x - ((event.modifiers & Qt.ShiftModifier) ? 1 : keyboardMoveStepSize));
changePosition();
} else {
event.accepted = false;
}
Keys.onRightPressed: if (!dialogRoot.vertical) {
root.x = Math.min(root.maximumPosition, root.x + ((event.modifiers & Qt.ShiftModifier) ? 1 : keyboardMoveStepSize));
changePosition();
} else {
event.accepted = false;
}
// END Arrow keys
onPositionChanged: {
if (!drag.active) {
return;
}
changePosition();
}
onDoubleClicked: {
root.value = root.defaultPosition();
}
function changePosition() {
root.hasEverBeenMoved = true
if (dialogRoot.vertical) {
if (root.alignment === Qt.AlignRight) {
root.value = root.parent.height - (root.y + offset + root.height/2)
} else if (alignment === Qt.AlignLeft) {
root.value = root.y - offset + root.height/2
//Center
} else {
if (root.alignment & Qt.AlignRight) {
root.value = (root.parent.height/2 - root.y + offset)*2 - root.height
} else if (root.alignment & Qt.AlignLeft) {
root.value = (root.y - offset - root.parent.height/2)*2 + root.height
} else {
var value = root.y - root.parent.height/2 - offset + root.height/2
//Snap
if (Math.abs(value) < 5) {
root.value = 0
} else {
root.value = value
}
}
}
} else {
if (root.alignment === Qt.AlignRight) {
root.value = root.parent.width - (root.x + offset + root.width/2)
} else if (alignment === Qt.AlignLeft) {
root.value = root.x - offset + root.width/2
//Center
} else {
if (root.alignment & Qt.AlignRight) {
root.value = (root.parent.width/2 - root.x + offset)*2 - root.width
} else if (root.alignment & Qt.AlignLeft) {
root.value = (root.x - offset - root.parent.width/2)*2 + root.width
} else {
var value = root.x - root.parent.width/2 - offset + root.width/2
//Snap
if (Math.abs(value) < 5) {
root.value = 0
} else {
root.value = value
}
}
}
}
}
PlasmaExtras.Highlight {
anchors.fill: parent
visible: parent.activeFocus
hovered: true
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View file

@ -0,0 +1,273 @@
/*
SPDX-FileCopyrightText: 2021 Cyril Rossi <cyril.rossi@enioka.com>
SPDX-FileCopyrightText: 2022 Marco Martin <mart@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.4 as QQC2
import QtQuick.Layouts 1.3
import QtQuick.Window 2.15
import org.kde.kirigami 2.13 as Kirigami
QQC2.Control {
id: delegate
property Item viewPort
readonly property string screenName: model.screenName
readonly property int screenId: model.screenId
property bool containsDrag
property alias contentsLayout: contentsLayout
width: Math.min(Kirigami.Units.gridUnit * 25, Math.floor(viewPort.width / Math.min(repeater.count, Math.floor(viewPort.width / (Kirigami.Units.gridUnit * 12)))))
contentItem: ColumnLayout {
id: contentsLayout
width: Math.min(parent.width, Kirigami.Units.gridUnit * 15)
Rectangle {
id: screenRect
Layout.fillWidth: true
Layout.preferredHeight: width / 1.6
color: Kirigami.Theme.backgroundColor
border.color: Kirigami.Theme.textColor
Rectangle {
anchors.fill: parent
z: 9
color: "black"
opacity: delegate.containsDrag ? 0.3 : 0
Behavior on opacity {
OpacityAnimator {
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
}
}
}
Repeater {
id: containmentRepeater
model: containments
Rectangle {
id: contRect
property real homeX
property real homeY
property string oldState
readonly property int edgeDistance: {
return (state === "left" || state === "right" ? width : height) * model.edgePosition;
}
width: moveButton.width
height: moveButton.height
border.color: Kirigami.Theme.textColor
color: Kirigami.Theme.backgroundColor
state: model.edge
z: state === "floating" ? 0 : 1
visible: !model.isDestroyed
HoverHandler {
cursorShape: Qt.OpenHandCursor
}
DragHandler {
id: dragHandler
property QQC2.Control targetDelegate
cursorShape: Qt.ClosedHandCursor
onActiveChanged: {
if (active) {
delegate.z = 1;
} else {
if (targetDelegate) {
resetAnim.restart();
containmentRepeater.model.moveContainementToScreen(model.containmentId, targetDelegate.screenId)
targetDelegate.containsDrag = false;
targetDelegate = null;
} else {
resetAnim.restart();
}
}
}
onTranslationChanged: {
if (!active) {
if (targetDelegate) {
targetDelegate.containsDrag = false;
targetDelegate = null;
}
return;
}
let pos = contRect.mapToItem(delegate.parent, dragHandler.centroid.position.x, dragHandler.centroid.position.y);
let otherDelegate = delegate.parent.childAt(pos.x, pos.y);
if (targetDelegate && targetDelegate !== otherDelegate) {
targetDelegate.containsDrag = false;
}
if (!otherDelegate || otherDelegate === delegate) {
targetDelegate = null;
} else if (otherDelegate && otherDelegate !== delegate
&& otherDelegate.hasOwnProperty("screenId")
&& otherDelegate.hasOwnProperty("containsDrag")) {
targetDelegate = otherDelegate;
targetDelegate.containsDrag = true;
}
}
}
SequentialAnimation {
id: resetAnim
property var targetDelegatePos: dragHandler.targetDelegate
? dragHandler.targetDelegate.contentsLayout.mapToItem(delegate.contentsLayout, 0, 0)
: Qt.point(0, 0)
ParallelAnimation {
XAnimator {
target: contRect
from: contRect.x
to: contRect.homeX + resetAnim.targetDelegatePos.x
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
}
YAnimator {
target: contRect
from: contRect.y
to: contRect.homeY + resetAnim.targetDelegatePos.y
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
}
}
PropertyAction {
target: delegate
property: "z"
value: 0
}
}
Image {
id: containmentImage
anchors {
fill: parent
margins: 1
}
// It needs to reload the image from disk when the file changes
cache: false
source: model.imageSource
fillMode: model.edge == "floating" ? Image.PreserveAspectCrop : Image.PreserveAspectFit
}
QQC2.Button {
id: moveButton
icon.name: "open-menu-symbolic"
checked: contextMenu.visible
anchors {
right: parent.right
top: parent.top
topMargin: model.edge == "floating"
? model.panelCountAtTop * moveButton.height + Kirigami.Units.largeSpacing
: 0
rightMargin: model.edge == "floating"
? (moveButton.LayoutMirroring.enabled ? model.panelCountAtLeft : model.panelCountAtRight) * moveButton.height + Kirigami.Units.largeSpacing
: 0
}
onClicked: {
contextMenu.open()
}
QQC2.Menu {
id: contextMenu
y: moveButton.height
Repeater {
model: ShellContainmentModel
QQC2.MenuItem {
text: edge == "floating"
? i18nd("plasma_shell_org.kde.plasma.desktop", "Swap with Desktop on Screen %1", model.screenName)
: i18nd("plasma_shell_org.kde.plasma.desktop", "Move to Screen %1", model.screenName)
visible: model.screenName !== delegate.screenName
height: visible ? implicitHeight : 0
onTriggered: {
containmentRepeater.model.moveContainementToScreen(containmentId, screenId)
}
}
}
QQC2.MenuSeparator {
visible: removeItem.visible
}
QQC2.MenuItem {
id: removeItem
text: contRect.state === "floating"
? i18nd("plasma_shell_org.kde.plasma.desktop", "Remove Desktop")
: i18nd("plasma_shell_org.kde.plasma.desktop", "Remove Panel")
icon.name: "edit-delete"
onTriggered: {
if (contRect.state === "floating") {
ShellContainmentModel.remove(screenId);
} else {
containments.remove(containmentId);
}
}
visible: contRect.state !== "floating" || !model.active
}
}
}
states: [
State {
name: "floating"
PropertyChanges {
target: contRect;
width: screenRect.width
height: screenRect.height
color: "transparent"
}
},
State {
name: "top"
PropertyChanges {
target: contRect;
width: screenRect.width
y: homeY
homeX: 0
homeY: contRect.edgeDistance
}
},
State {
name: "right"
PropertyChanges {
target: contRect;
x: homeX
homeX: screenRect.width - contRect.width - contRect.edgeDistance;
height: screenRect.height
homeY: 0
}
},
State {
name: "bottom"
PropertyChanges {
target: contRect;
y: homeY
homeX: 0
homeY: screenRect.height - contRect.height - contRect.edgeDistance;
width: screenRect.width
}
},
State {
name: "left"
PropertyChanges {
target: contRect;
height: screenRect.height
x: homeX
homeX: contRect.edgeDistance
homeY: 0
}
}
]
}
}
}
QQC2.Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
text: model.isPrimary
? i18nd("plasma_shell_org.kde.plasma.desktop", "%1 (primary)", model.screenName)
: model.screenName
textFormat: Text.PlainText
}
}
}