mirror of
https://github.com/tumillanino/miasma-os.git
synced 2026-04-11 07:15:31 +00:00
some more branding
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Aaron Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.plasma.private.shell
|
||||
|
||||
Item {
|
||||
id: main
|
||||
|
||||
property string scriptPath
|
||||
property alias mode: interactiveConsole.mode
|
||||
signal visibleChanged(bool visible)
|
||||
|
||||
onScriptPathChanged: {
|
||||
interactiveConsole.loadScript(scriptPath);
|
||||
}
|
||||
|
||||
InteractiveConsoleWindow {
|
||||
id: interactiveConsole
|
||||
|
||||
onVisibleChanged: {
|
||||
main.visibleChanged(visible);
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
interactiveConsole.scriptEngine = scriptEngine;
|
||||
interactiveConsole.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014-2020 Ivan Cukic <ivan.cukic(at)kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.plasma.activityswitcher as ActivitySwitcher
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
import org.kde.kcmutils // KCMLauncher
|
||||
|
||||
import "static.js" as S
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property int innerPadding : Kirigami.Units.gridUnit
|
||||
|
||||
property bool current : false
|
||||
property bool selected : false
|
||||
|
||||
property alias title : title.text
|
||||
property alias icon : icon.source
|
||||
property alias hasWindows : hasWindowsIndicator.visible
|
||||
|
||||
z : current ? 10 :
|
||||
selected ? 5 : 0
|
||||
|
||||
property string activityId : ""
|
||||
|
||||
property string background : ""
|
||||
|
||||
onBackgroundChanged: if (background[0] !== '#') {
|
||||
// We have a proper wallpaper, hurroo!
|
||||
backgroundColor.visible = false;
|
||||
|
||||
} else {
|
||||
// We have only a color
|
||||
backgroundColor.color = background;
|
||||
backgroundColor.visible = true;
|
||||
}
|
||||
|
||||
signal clicked
|
||||
|
||||
width : 200
|
||||
height : width * 9.0 / 16.0
|
||||
|
||||
Item {
|
||||
anchors {
|
||||
fill: parent
|
||||
}
|
||||
|
||||
// Background until we get something real
|
||||
Rectangle {
|
||||
id: backgroundColor
|
||||
|
||||
anchors.fill: parent
|
||||
// This is intentional - while waiting for the wallpaper,
|
||||
// we are showing a semi-transparent black background
|
||||
color: "black"
|
||||
|
||||
opacity: root.selected ? .8 : .5
|
||||
}
|
||||
|
||||
Image {
|
||||
id: backgroundWallpaper
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
visible: !backgroundColor.visible
|
||||
source: "image://wallpaperthumbnail/" + root.background
|
||||
sourceSize: Qt.size(width, height)
|
||||
}
|
||||
|
||||
// Title and the icon
|
||||
|
||||
Rectangle {
|
||||
id: shade
|
||||
|
||||
width: parent.height
|
||||
height: parent.width
|
||||
|
||||
anchors.centerIn: parent
|
||||
rotation: 90
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 1.0; color: "black" }
|
||||
GradientStop { position: 0.0; color: "transparent" }
|
||||
}
|
||||
|
||||
opacity : root.selected ? 0.5 : 1.0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: currentActivityHighlight
|
||||
|
||||
visible: root.current
|
||||
|
||||
border.width: root.current ? Kirigami.Units.smallSpacing : 0
|
||||
border.color: Kirigami.Theme.highlightColor
|
||||
|
||||
z: 10
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
// Hide the rounding error on the bottom of the rectangle
|
||||
bottomMargin: -1
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
}
|
||||
|
||||
Item {
|
||||
id: titleBar
|
||||
|
||||
anchors {
|
||||
top : parent.top
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
|
||||
leftMargin : 2 * Kirigami.Units.smallSpacing + 2
|
||||
topMargin : 2 * Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
PlasmaExtras.ShadowedLabel {
|
||||
id: title
|
||||
|
||||
color : "white"
|
||||
elide : Text.ElideRight
|
||||
visible : shade.visible
|
||||
|
||||
font.bold : true
|
||||
|
||||
anchors {
|
||||
top : parent.top
|
||||
left : parent.left
|
||||
right : icon.left
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaExtras.ShadowedLabel {
|
||||
id: description
|
||||
|
||||
color : "white"
|
||||
elide : Text.ElideRight
|
||||
text : model.description
|
||||
|
||||
anchors {
|
||||
top : title.bottom
|
||||
left : parent.left
|
||||
right : icon.left
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
id: icon
|
||||
|
||||
width : Kirigami.Units.iconSizes.medium
|
||||
height : width
|
||||
|
||||
anchors {
|
||||
right : parent.right
|
||||
rightMargin : 2 * Kirigami.Units.smallSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: statsBar
|
||||
|
||||
height: childrenRect.height + Kirigami.Units.smallSpacing
|
||||
|
||||
anchors {
|
||||
bottom : controlBar.top
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
|
||||
leftMargin : 2 * Kirigami.Units.smallSpacing + 2
|
||||
rightMargin : 2 * Kirigami.Units.smallSpacing
|
||||
bottomMargin : Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
id : hasWindowsIndicator
|
||||
source : "window-duplicate"
|
||||
width : 16
|
||||
height : width
|
||||
opacity : .6
|
||||
visible : false
|
||||
}
|
||||
|
||||
PlasmaExtras.ShadowedLabel {
|
||||
id: lastUsedDate
|
||||
|
||||
color : "white"
|
||||
elide : Text.ElideRight
|
||||
|
||||
text: root.current ?
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:status currently active activity", "Currently being used") :
|
||||
model.lastTimeUsedString
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: dropHighlight
|
||||
visible: moveDropAction.isHovered || copyDropAction.isHovered
|
||||
|
||||
onVisibleChanged: {
|
||||
ActivitySwitcher.Backend.setDropMode(visible);
|
||||
if (visible) {
|
||||
root.state = "dropAreasShown";
|
||||
} else {
|
||||
root.state = "plain";
|
||||
}
|
||||
}
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: icon.height + 3 * Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
opacity: .75
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
}
|
||||
|
||||
TaskDropArea {
|
||||
id: moveDropAction
|
||||
|
||||
anchors {
|
||||
right: parent.horizontalCenter
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
topPadding: icon.height + 3 * Kirigami.Units.smallSpacing
|
||||
actionVisible: dropHighlight.visible
|
||||
|
||||
actionTitle: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action shows when dragging file over item", "Move to\nthis activity")
|
||||
|
||||
onTaskDropped: mimeData => {
|
||||
ActivitySwitcher.Backend.dropMove(mimeData, root.activityId);
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked();
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
S.showActivityItemActionsBar(root);
|
||||
}
|
||||
|
||||
visible: ActivitySwitcher.Backend.dropEnabled
|
||||
}
|
||||
|
||||
TaskDropArea {
|
||||
id: copyDropAction
|
||||
|
||||
topPadding: icon.height + 3 * Kirigami.Units.smallSpacing
|
||||
actionVisible: dropHighlight.visible
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.horizontalCenter
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
actionTitle: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action shows when dragging file over item", "Show also\nin this activity")
|
||||
|
||||
onTaskDropped: mimeData => {
|
||||
ActivitySwitcher.Backend.dropCopy(mimeData, root.activityId);
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked();
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
S.showActivityItemActionsBar(root);
|
||||
}
|
||||
|
||||
visible: ActivitySwitcher.Backend.dropEnabled
|
||||
|
||||
}
|
||||
|
||||
// Controls
|
||||
Item {
|
||||
id: controlBar
|
||||
|
||||
height: root.state == "showingControls" ?
|
||||
(configButton.height + 4 * Kirigami.Units.smallSpacing) :
|
||||
0
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.longDuration
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
anchors {
|
||||
bottom : parent.bottom
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: - 2 * Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
opacity: .75
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
}
|
||||
|
||||
PlasmaComponents.Button {
|
||||
id: configButton
|
||||
|
||||
icon.name: "configure"
|
||||
PlasmaComponents.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
PlasmaComponents.ToolTip.visible: hovered
|
||||
PlasmaComponents.ToolTip.text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button tooltip only, opens kcm", "Configure")
|
||||
|
||||
onClicked: KCMLauncher.openSystemSettings("kcm_activities", root.activityId);
|
||||
|
||||
anchors {
|
||||
left : parent.left
|
||||
top : parent.top
|
||||
leftMargin : 2 * Kirigami.Units.smallSpacing + 2
|
||||
topMargin : 2 * Kirigami.Units.smallSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "plain"
|
||||
PropertyChanges { shade.visible: true }
|
||||
PropertyChanges { controlBar.opacity: 0 }
|
||||
},
|
||||
State {
|
||||
name: "showingControls"
|
||||
PropertyChanges { shade.visible: true }
|
||||
PropertyChanges { controlBar.opacity: 1 }
|
||||
},
|
||||
State {
|
||||
name: "dropAreasShown"
|
||||
// PropertyChanges { shade.visible: false }
|
||||
PropertyChanges { statsBar.visible: false }
|
||||
PropertyChanges { controlBar.opacity: 0 }
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
NumberAnimation {
|
||||
properties : "opacity"
|
||||
duration : Kirigami.Units.shortDuration
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/* vim:set foldmethod=marker:
|
||||
|
||||
SPDX-FileCopyrightText: 2014 Ivan Cukic <ivan.cukic(at)kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.plasma.activityswitcher as ActivitySwitcher
|
||||
|
||||
Flickable {
|
||||
id: root
|
||||
|
||||
// contentWidth: content.width
|
||||
contentHeight: content.height
|
||||
|
||||
property var model: ActivitySwitcher.Backend.runningActivitiesModel()
|
||||
property string filterString: ""
|
||||
property bool showSwitcherOnly: false
|
||||
|
||||
property int itemsWidth: 0
|
||||
|
||||
property int selectedIndex: -1
|
||||
|
||||
function _selectRelativeToCurrent(distance)
|
||||
{
|
||||
var startingWithSelected = selectedIndex;
|
||||
var visited = 0;
|
||||
|
||||
do {
|
||||
selectedIndex += distance;
|
||||
visited++;
|
||||
|
||||
if (selectedIndex >= activitiesList.count) {
|
||||
selectedIndex = 0;
|
||||
}
|
||||
|
||||
if (selectedIndex < 0) {
|
||||
selectedIndex = activitiesList.count - 1;
|
||||
}
|
||||
|
||||
if (visited >= activitiesList.count) {
|
||||
// we've visited all activities but could not find a visible
|
||||
// one, so stop searching and select the one we started with
|
||||
selectedIndex = startingWithSelected;
|
||||
break;
|
||||
}
|
||||
|
||||
// Searching for the first item that is visible
|
||||
} while (!activitiesList.itemAt(selectedIndex).visible);
|
||||
_updateSelectedItem();
|
||||
|
||||
}
|
||||
|
||||
function selectNext()
|
||||
{
|
||||
_selectRelativeToCurrent(1);
|
||||
}
|
||||
|
||||
function selectPrevious()
|
||||
{
|
||||
_selectRelativeToCurrent(-1);
|
||||
}
|
||||
|
||||
function _updateSelectedItem()
|
||||
{
|
||||
for (var i = 0; i < activitiesList.count; i++) {
|
||||
activitiesList.itemAt(i).selected = (i === selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
function openSelected()
|
||||
{
|
||||
var selectedItem = null;
|
||||
|
||||
if (selectedIndex >= 0 && selectedIndex < activitiesList.count) {
|
||||
selectedItem = activitiesList.itemAt(selectedIndex) as ActivityItem;
|
||||
|
||||
} else if (root.filterString != "") {
|
||||
// If we have only one item shown, activate it. It doesn't matter
|
||||
// that it is not really selected
|
||||
|
||||
for (var i = 0; i < activitiesList.count; i++) {
|
||||
var item = activitiesList.itemAt(i) as ActivityItem;
|
||||
|
||||
if (item.visible) {
|
||||
selectedItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedItem !== null) {
|
||||
ActivitySwitcher.Backend.setCurrentActivity(selectedItem.activityId);
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: content
|
||||
|
||||
// width: root.width - (root.width % 10)
|
||||
width: root.itemsWidth
|
||||
spacing: Kirigami.Units.smallSpacing * 2
|
||||
|
||||
Repeater {
|
||||
id: activitiesList
|
||||
|
||||
model: ActivitySwitcher.Backend.runningActivitiesModel()
|
||||
|
||||
ActivityItem {
|
||||
|
||||
width: content.width
|
||||
|
||||
visible : (root.filterString == "") ||
|
||||
(title.toLowerCase().indexOf(root.filterString) != -1)
|
||||
|
||||
activityId : model.id
|
||||
title : model.name
|
||||
icon : model.iconSource
|
||||
background : model.background
|
||||
current : model.isCurrent
|
||||
hasWindows : model.hasWindows
|
||||
innerPadding : 2 * Kirigami.Units.smallSpacing
|
||||
|
||||
onClicked : {
|
||||
ActivitySwitcher.Backend.setCurrentActivity(model.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add: Transition {
|
||||
NumberAnimation {
|
||||
properties: "x"
|
||||
from: -100
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
move: Transition {
|
||||
NumberAnimation {
|
||||
id: animation
|
||||
properties: "y"
|
||||
duration: Kirigami.Units.longDuration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
SPDX-FileCopyrightText: 2014 Ivan Cukic <ivan.cukic@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
import org.kde.config as KConfig // KAuthorized
|
||||
import org.kde.kcmutils // KCMLauncher
|
||||
|
||||
|
||||
FocusScope {
|
||||
id: root
|
||||
signal closed()
|
||||
|
||||
//this is used to perfectly align the filter field and delegates
|
||||
property int cellWidth: Kirigami.Units.iconSizes.sizeForLabels * 30
|
||||
property int spacing: 2 * Kirigami.Units.smallSpacing
|
||||
|
||||
property bool showSwitcherOnly: false
|
||||
|
||||
width: Kirigami.Units.gridUnit * 16
|
||||
|
||||
Item {
|
||||
id: activityBrowser
|
||||
|
||||
property int spacing: 2 * Kirigami.Units.smallSpacing
|
||||
|
||||
signal closeRequested()
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
if (heading.searchString.length > 0) {
|
||||
heading.searchString = "";
|
||||
} else {
|
||||
activityBrowser.closeRequested();
|
||||
}
|
||||
|
||||
} else if (event.key === Qt.Key_Up) {
|
||||
activityList.selectPrevious();
|
||||
|
||||
} else if (event.key === Qt.Key_Down) {
|
||||
activityList.selectNext();
|
||||
|
||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
activityList.openSelected();
|
||||
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
// console.log("TAB KEY");
|
||||
|
||||
} else {
|
||||
// console.log("OTHER KEY");
|
||||
// event.accepted = false;
|
||||
// heading.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
// Rectangle {
|
||||
// anchors.fill : parent
|
||||
// opacity : .4
|
||||
// color : "white"
|
||||
// }
|
||||
|
||||
Heading {
|
||||
id: heading
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
|
||||
leftMargin: Kirigami.Units.smallSpacing
|
||||
rightMargin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
visible: !root.showSwitcherOnly
|
||||
|
||||
onCloseRequested: activityBrowser.closeRequested()
|
||||
}
|
||||
|
||||
PlasmaComponents.ScrollView {
|
||||
anchors {
|
||||
top: heading.visible ? heading.bottom : parent.top
|
||||
bottom: bottomPanel.visible ? bottomPanel.top : parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
topMargin: activityBrowser.spacing
|
||||
}
|
||||
|
||||
ActivityList {
|
||||
id: activityList
|
||||
showSwitcherOnly: root.showSwitcherOnly
|
||||
filterString: heading.searchString.toLowerCase()
|
||||
itemsWidth: root.width - Kirigami.Units.smallSpacing
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: bottomPanel
|
||||
|
||||
height: newActivityButton.height + Kirigami.Units.gridUnit
|
||||
|
||||
visible: !root.showSwitcherOnly
|
||||
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
id: newActivityButton
|
||||
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button opens Activity kcm", "Create activity…")
|
||||
icon.name: "list-add"
|
||||
|
||||
width: parent.width
|
||||
|
||||
onClicked: KCMLauncher.openSystemSettings("kcm_activities", "newActivity")
|
||||
|
||||
visible: KConfig.KAuthorized.authorize("plasma-desktop/add_activities")
|
||||
}
|
||||
}
|
||||
|
||||
onCloseRequested: root.closed()
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/* vim:set foldmethod=marker:
|
||||
|
||||
SPDX-FileCopyrightText: 2014 Ivan Cukic <ivan.cukic(at)kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.components as PlasmaComponents
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.kcmutils as KCM
|
||||
import org.kde.config as KConfig
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias searchString: searchText.text
|
||||
property bool showingSearch: false
|
||||
|
||||
signal closeRequested
|
||||
|
||||
function focusSearch() {
|
||||
searchText.forceActiveFocus()
|
||||
}
|
||||
|
||||
onShowingSearchChanged: if (!showingSearch) searchText.text = ""
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
if (root.showingSearch) {
|
||||
event.accepted = true;
|
||||
root.showingSearch = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
height: childrenRect.height
|
||||
|
||||
RowLayout {
|
||||
id: buttonRow
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.Heading {
|
||||
id: heading
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
level: 1
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:window", "Activities")
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
|
||||
visible: !root.showingSearch
|
||||
}
|
||||
|
||||
PlasmaExtras.SearchField {
|
||||
id: searchText
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
focus: true
|
||||
visible: root.showingSearch
|
||||
|
||||
onTextChanged: if (text != "") root.showingSearch = true
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
id: searchButton
|
||||
icon.name: "edit-find"
|
||||
|
||||
// checkable: true
|
||||
// onClicked: root.closeRequested()
|
||||
onClicked: root.showingSearch = !root.showingSearch
|
||||
checked: root.showingSearch
|
||||
}
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
id: configureButton
|
||||
icon.name: "configure"
|
||||
visible: KConfig.KAuthorized.authorizeControlModule("kcm_activities")
|
||||
onClicked: {
|
||||
KCM.KCMLauncher.openSystemSettings("kcm_activities");
|
||||
root.closeRequested();
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
id: closeButton
|
||||
icon.name: "window-close"
|
||||
onClicked: root.closeRequested()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Ivan Cukic <ivan.cukic(at)kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.draganddrop as DND
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
|
||||
DND.DropArea {
|
||||
id: root
|
||||
|
||||
signal taskDropped(var mimeData, var modifiers)
|
||||
signal clicked()
|
||||
signal entered()
|
||||
|
||||
property int topPadding: 0
|
||||
property string activityName: ""
|
||||
property bool selected: false
|
||||
property string actionTitle: ""
|
||||
property bool isHovered: false
|
||||
property bool actionVisible: false
|
||||
|
||||
PlasmaExtras.Highlight {
|
||||
id: dropHighlight
|
||||
anchors {
|
||||
fill: parent
|
||||
// topMargin: icon.height + 3 * Kirigami.Units.smallSpacing
|
||||
topMargin: root.topPadding
|
||||
}
|
||||
visible: root.isHovered
|
||||
z: -1
|
||||
}
|
||||
|
||||
Text {
|
||||
id: dropAreaLeftText
|
||||
anchors {
|
||||
fill: dropHighlight
|
||||
leftMargin: Kirigami.Units.gridUnit
|
||||
rightMargin: Kirigami.Units.gridUnit
|
||||
}
|
||||
|
||||
color: Kirigami.Theme.textColor
|
||||
visible: root.actionVisible
|
||||
|
||||
text: root.actionTitle
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
maximumLineCount: 3
|
||||
}
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.horizontalCenter
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
preventStealing: true
|
||||
enabled: true
|
||||
|
||||
onDrop: event => {
|
||||
root.taskDropped(event.mimeData, event.modifiers);
|
||||
}
|
||||
|
||||
onDragEnter: {
|
||||
root.isHovered = true;
|
||||
}
|
||||
|
||||
onDragLeave: {
|
||||
root.isHovered = false;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.clicked()
|
||||
hoverEnabled: true
|
||||
onEntered: root.entered()
|
||||
|
||||
Accessible.name: root.activityName
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.selected: root.selected
|
||||
Accessible.onPressAction: root.clicked()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Ivan Cukic <ivan.cukic(at)kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Image {
|
||||
id: main
|
||||
|
||||
width: 480
|
||||
height: 425
|
||||
|
||||
source: "images/window2.png"
|
||||
anchors.centerIn: parent
|
||||
|
||||
Item {
|
||||
id: title
|
||||
|
||||
width: titleText.width + 32
|
||||
height: titleText.height + 32
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
opacity: .7
|
||||
}
|
||||
|
||||
Text {
|
||||
id: titleText
|
||||
color: "white"
|
||||
text: "Firefox"
|
||||
font.pointSize: 24
|
||||
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
Drag.active: mouseArea.drag.active
|
||||
Drag.hotSpot.x: 32
|
||||
Drag.hotSpot.y: 32
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
|
||||
drag {
|
||||
target: title
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/* vim:set foldmethod=marker:
|
||||
|
||||
SPDX-FileCopyrightText: 2015 Ivan Cukic <ivan.cukic(at)kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
var currentlyHoveredActivityItem = null;
|
||||
|
||||
function showActivityItemActionsBar(activityItem) {
|
||||
if (activityItem === currentlyHoveredActivityItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentlyHoveredActivityItem !== null) {
|
||||
currentlyHoveredActivityItem.state = "plain";
|
||||
}
|
||||
|
||||
currentlyHoveredActivityItem = activityItem;
|
||||
currentlyHoveredActivityItem.state = "showingControls";
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls as QQC2
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.components as PC3
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.plasmoid
|
||||
|
||||
PlasmoidItem {
|
||||
id: root
|
||||
|
||||
enum LayoutType {
|
||||
HorizontalPanel,
|
||||
VerticalPanel,
|
||||
Desktop,
|
||||
DesktopCompact
|
||||
}
|
||||
|
||||
property var errorInformation
|
||||
|
||||
readonly property real minimumPreferredWidth: Kirigami.Units.gridUnit * 12
|
||||
readonly property real minimumPreferredHeight: Kirigami.Units.gridUnit * 12
|
||||
|
||||
// To properly show the error message in panel
|
||||
readonly property int layoutForm: {
|
||||
if (fullRepresentationItem.width >= root.minimumPreferredWidth) {
|
||||
if (fullRepresentationItem.height >= root.minimumPreferredHeight) {
|
||||
return AppletError.Desktop;
|
||||
} else if (fullRepresentationItem.height >= Kirigami.Units.iconSizes.huge + root.fullRepresentationItem.buttonLayout.implicitHeight) {
|
||||
return AppletError.DesktopCompact;
|
||||
}
|
||||
}
|
||||
|
||||
return Plasmoid.formFactor === PlasmaCore.Types.Vertical ? AppletError.VerticalPanel : AppletError.HorizontalPanel;
|
||||
}
|
||||
preloadFullRepresentation: true
|
||||
fullRepresentation: GridLayout {
|
||||
id: fullRep
|
||||
property alias buttonLayout: buttonLayout
|
||||
Layout.minimumWidth: {
|
||||
switch (root.layoutForm) {
|
||||
case AppletError.Desktop:
|
||||
case AppletError.DesktopCompact:
|
||||
// [Icon] [Text]
|
||||
// [Button]
|
||||
// [Information]
|
||||
return Math.max(root.minimumPreferredWidth, buttonLayout.implicitWidth);
|
||||
case AppletError.VerticalPanel:
|
||||
// [Icon]
|
||||
// [Copy]
|
||||
// [Open]
|
||||
return Math.max(headerIcon.implicitWidth, buttonLayout.implicitWidth);
|
||||
case AppletError.HorizontalPanel:
|
||||
// [Icon] [Copy] [Open]
|
||||
return headingLayout.implicitWidth + rowSpacing + buttonLayout.implicitWidth;
|
||||
}
|
||||
}
|
||||
Layout.minimumHeight: {
|
||||
switch (root.layoutForm) {
|
||||
case AppletError.Desktop:
|
||||
return headingLayout.implicitHeight + fullRep.columnSpacing + buttonLayout.implicitHeight + fullRep.columnSpacing + fullContentView.implicitHeight;
|
||||
case AppletError.DesktopCompact:
|
||||
return Math.max(headingLayout.implicitHeight, buttonLayout.implicitHeight);
|
||||
case AppletError.VerticalPanel:
|
||||
return headingLayout.implicitHeight + fullRep.columnSpacing + buttonLayout.implicitHeight;
|
||||
case AppletError.HorizontalPanel:
|
||||
return Math.max(headingLayout.implicitHeight, buttonLayout.implicitHeight);
|
||||
}
|
||||
}
|
||||
// Same as systray popups
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 24
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 24
|
||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 34
|
||||
Layout.maximumHeight: Kirigami.Units.gridUnit * 34
|
||||
|
||||
rowSpacing: textArea.topPadding
|
||||
columnSpacing: rowSpacing
|
||||
flow: {
|
||||
switch (root.layoutForm) {
|
||||
case AppletError.HorizontalPanel:
|
||||
return GridLayout.LeftToRight;
|
||||
default:
|
||||
return GridLayout.TopToBottom;
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: headingLayout
|
||||
|
||||
Layout.margins: root.layoutForm !== AppletError.Desktop ? 0 : Kirigami.Units.gridUnit
|
||||
Layout.maximumWidth: fullRep.width
|
||||
spacing: 0
|
||||
Layout.fillWidth: true
|
||||
|
||||
Kirigami.Icon {
|
||||
id: headerIcon
|
||||
implicitWidth: Math.min(Kirigami.Units.iconSizes.huge, fullRep.width, fullRep.height)
|
||||
implicitHeight: implicitWidth
|
||||
|
||||
activeFocusOnTab: true
|
||||
source: "dialog-error"
|
||||
|
||||
Accessible.description: heading.text
|
||||
|
||||
PlasmaCore.ToolTipArea {
|
||||
anchors.fill: parent
|
||||
enabled: !heading.visible || heading.truncated
|
||||
mainText: heading.text
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
id: heading
|
||||
visible: root.layoutForm !== AppletError.VerticalPanel
|
||||
// Descent is equal to the amount of space above and below capital letters.
|
||||
// Add descent to the sides to make the spacing around Latin text look more even.
|
||||
leftPadding: headingFontMetrics.descent
|
||||
rightPadding: headingFontMetrics.descent
|
||||
text: root.errorInformation ? root.errorInformation.compactError : "No error information."
|
||||
textFormat: Text.PlainText
|
||||
level: 2
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumHeight: headerIcon.implicitHeight
|
||||
|
||||
FontMetrics {
|
||||
id: headingFontMetrics
|
||||
font: heading.font
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: buttonLayout
|
||||
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
rowSpacing: fullRep.rowSpacing
|
||||
columnSpacing: parent.columnSpacing
|
||||
flow: {
|
||||
switch (root.layoutForm) {
|
||||
case AppletError.HorizontalPanel:
|
||||
case AppletError.VerticalPanel:
|
||||
return fullRep.flow;
|
||||
default:
|
||||
return GridLayout.LeftToRight;
|
||||
}
|
||||
}
|
||||
|
||||
PC3.Button {
|
||||
id: copyButton
|
||||
display: root.layoutForm === AppletError.HorizontalPanel || root.layoutForm === AppletError.VerticalPanel ? PC3.AbstractButton.IconOnly : PC3.AbstractButton.TextBesideIcon
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Copy to Clipboard")
|
||||
icon.name: "edit-copy"
|
||||
onClicked: {
|
||||
textArea.selectAll()
|
||||
textArea.copy()
|
||||
textArea.deselect()
|
||||
}
|
||||
|
||||
PlasmaCore.ToolTipArea {
|
||||
anchors.fill: parent
|
||||
enabled: parent.display === PC3.AbstractButton.IconOnly
|
||||
mainText: parent.text
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: compactContentLoader
|
||||
active: root.layoutForm !== AppletError.Desktop
|
||||
visible: active
|
||||
sourceComponent: PC3.Button {
|
||||
display: copyButton.display
|
||||
icon.name: "window-new"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button opens dialog", "View Error Details…")
|
||||
checked: dialog.visible
|
||||
onClicked: dialog.visible = !dialog.visible
|
||||
|
||||
PlasmaCore.ToolTipArea {
|
||||
anchors.fill: parent
|
||||
enabled: parent.display === PC3.AbstractButton.IconOnly
|
||||
mainText: parent.text
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
QQC2.ApplicationWindow {
|
||||
id: dialog
|
||||
flags: Qt.Dialog | Qt.WindowStaysOnTopHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint
|
||||
minimumWidth: dialogFontMetrics.height * 12
|
||||
+ dialogTextArea.leftPadding + dialogTextArea.rightPadding
|
||||
minimumHeight: dialogFontMetrics.height * 12
|
||||
+ dialogTextArea.topPadding + dialogTextArea.bottomPadding
|
||||
width: Kirigami.Units.gridUnit * 24
|
||||
height: Kirigami.Units.gridUnit * 24
|
||||
color: palette.base
|
||||
QQC2.ScrollView {
|
||||
id: dialogScrollView
|
||||
anchors.fill: parent
|
||||
QQC2.TextArea {
|
||||
id: dialogTextArea
|
||||
// HACK: silence binding loop warnings.
|
||||
// contentWidth seems to be causing the binding loop,
|
||||
// but contentWidth is read-only and we have no control
|
||||
// over how it is calculated.
|
||||
implicitWidth: 0
|
||||
wrapMode: TextEdit.Wrap
|
||||
text: textArea.text
|
||||
font.family: "monospace"
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
background: null
|
||||
FontMetrics {
|
||||
id: dialogFontMetrics
|
||||
font: dialogTextArea.font
|
||||
}
|
||||
}
|
||||
background: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PC3.ScrollView {
|
||||
id: fullContentView
|
||||
// Not handled by a Loader because we need
|
||||
// TextEdit::copy() to copy to clipboard.
|
||||
visible: !compactContentLoader.active
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
PC3.TextArea {
|
||||
id: textArea
|
||||
// HACK: silence binding loop warnings.
|
||||
// contentWidth seems to be causing the binding loop,
|
||||
// but contentWidth is read-only and we have no control
|
||||
// over how it is calculated.
|
||||
implicitWidth: 0
|
||||
wrapMode: TextEdit.Wrap
|
||||
text: root.errorInformation && root.errorInformation.errors ?
|
||||
root.errorInformation.errors.join("\n\n")
|
||||
// This is just to suppress warnings. Users should never see this.
|
||||
: "No error information."
|
||||
font.family: "monospace"
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.ksvg as KSvg
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kquickcontrolsaddons
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
PlasmaCore.ToolTipArea {
|
||||
id: root
|
||||
objectName: "org.kde.desktop-CompactApplet"
|
||||
anchors.fill: parent
|
||||
|
||||
mainText: plasmoidItem.toolTipMainText
|
||||
subText: plasmoidItem.toolTipSubText
|
||||
location: Plasmoid.location
|
||||
active: !plasmoidItem.expanded
|
||||
textFormat: plasmoidItem.toolTipTextFormat
|
||||
mainItem: plasmoidItem.toolTipItem ? plasmoidItem.toolTipItem : null
|
||||
|
||||
readonly property bool vertical: location === PlasmaCore.Types.RightEdge || location === PlasmaCore.Types.LeftEdge
|
||||
|
||||
property Item fullRepresentation
|
||||
property Item compactRepresentation
|
||||
property Item expandedFeedback: expandedItem
|
||||
property PlasmoidItem plasmoidItem
|
||||
|
||||
onCompactRepresentationChanged: {
|
||||
if (compactRepresentation) {
|
||||
compactRepresentation.anchors.fill = null;
|
||||
compactRepresentation.parent = compactRepresentationParent;
|
||||
compactRepresentation.anchors.fill = compactRepresentationParent;
|
||||
compactRepresentation.visible = true;
|
||||
}
|
||||
root.visible = true;
|
||||
}
|
||||
|
||||
onFullRepresentationChanged: {
|
||||
if (fullRepresentation) {
|
||||
fullRepresentation.anchors.fill = null;
|
||||
fullRepresentation.parent = appletParent;
|
||||
fullRepresentation.anchors.fill = appletParent;
|
||||
|
||||
// This avoids the content being drawn underneath the
|
||||
// separator between the panel and the applet.
|
||||
if (!separator.visible) {
|
||||
return;
|
||||
}
|
||||
if (Plasmoid.location === PlasmaCore.Types.TopEdge) {
|
||||
fullRepresentation.anchors.topMargin = separator.height
|
||||
} else if (Plasmoid.location === PlasmaCore.Types.BottomEdge) {
|
||||
fullRepresentation.anchors.bottomMargin = separator.height
|
||||
} else if (Plasmoid.location === PlasmaCore.Types.LeftEdge) {
|
||||
fullRepresentation.anchors.leftMargin = separator.width
|
||||
} else if (Plasmoid.location === PlasmaCore.Types.RightEdge) {
|
||||
fullRepresentation.anchors.rightMargin = separator.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
id: compactRepresentationParent
|
||||
anchors.fill: parent
|
||||
activeFocusOnTab: true
|
||||
onActiveFocusChanged: {
|
||||
// When the scope gets the active focus, try to focus its first descendant,
|
||||
// if there is on which has activeFocusOnTab
|
||||
if (!activeFocus) {
|
||||
return;
|
||||
}
|
||||
let nextItem = nextItemInFocusChain();
|
||||
let candidate = nextItem;
|
||||
while (candidate.parent) {
|
||||
if (candidate === compactRepresentationParent) {
|
||||
nextItem.forceActiveFocus();
|
||||
return;
|
||||
}
|
||||
candidate = candidate.parent;
|
||||
}
|
||||
}
|
||||
|
||||
objectName: "expandApplet"
|
||||
Accessible.name: root.mainText
|
||||
Accessible.description: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:whatsthis Accessible description for panel widget %1", "Open %1", root.subText)
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.onPressAction: Plasmoid.activated()
|
||||
|
||||
Keys.onPressed: event => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Space:
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Select:
|
||||
Plasmoid.activated();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KSvg.FrameSvgItem {
|
||||
id: expandedItem
|
||||
z: -100
|
||||
|
||||
property var containerMargins: {
|
||||
let item = root;
|
||||
while (item.parent) {
|
||||
item = item.parent;
|
||||
if (item.isAppletContainer) {
|
||||
return item.getMargins;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
property bool returnAllMargins: true
|
||||
// The above makes sure margin is returned even for side margins, that
|
||||
// would be otherwise turned off.
|
||||
bottomMargin: !vertical && containerMargins ? -containerMargins('bottom', returnAllMargins) : 0
|
||||
topMargin: !vertical && containerMargins ? -containerMargins('top', returnAllMargins) : 0
|
||||
leftMargin: vertical && containerMargins ? -containerMargins('left', returnAllMargins) : 0
|
||||
rightMargin: vertical && containerMargins ? -containerMargins('right', returnAllMargins) : 0
|
||||
}
|
||||
imagePath: "widgets/tabbar"
|
||||
visible: opacity > 0
|
||||
prefix: {
|
||||
let prefix;
|
||||
switch (Plasmoid.location) {
|
||||
case PlasmaCore.Types.LeftEdge:
|
||||
prefix = "west-active-tab";
|
||||
break;
|
||||
case PlasmaCore.Types.TopEdge:
|
||||
prefix = "north-active-tab";
|
||||
break;
|
||||
case PlasmaCore.Types.RightEdge:
|
||||
prefix = "east-active-tab";
|
||||
break;
|
||||
default:
|
||||
prefix = "south-active-tab";
|
||||
}
|
||||
if (!hasElementPrefix(prefix)) {
|
||||
prefix = "active-tab";
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
opacity: root.plasmoidItem.expanded ? 1 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.shortDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: expandedSync
|
||||
interval: 100
|
||||
onTriggered: root.plasmoidItem.expanded = dialog.visible;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Plasmoid.internalAction("configure")
|
||||
function onTriggered() {
|
||||
if (root.plasmoidItem.hideOnWindowDeactivate) {
|
||||
root.plasmoidItem.expanded = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.Plasmoid
|
||||
function onContextualActionsAboutToShow() { root.hideImmediately() }
|
||||
}
|
||||
|
||||
PlasmaCore.AppletPopup {
|
||||
id: dialog
|
||||
objectName: "popupWindow"
|
||||
|
||||
popupDirection: {
|
||||
switch (Plasmoid.location) {
|
||||
case PlasmaCore.Types.TopEdge:
|
||||
return Qt.BottomEdge
|
||||
case PlasmaCore.Types.LeftEdge:
|
||||
return Qt.RightEdge
|
||||
case PlasmaCore.Types.RightEdge:
|
||||
return Qt.LeftEdge
|
||||
default:
|
||||
return Qt.TopEdge
|
||||
}
|
||||
}
|
||||
margin: (Plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentPrefersFloatingApplets) ? Kirigami.Units.largeSpacing : 0
|
||||
|
||||
Behavior on margin {
|
||||
NumberAnimation {
|
||||
// Since the panel animation won't be perfectly in sync,
|
||||
// using a duration larger than the panel animation results
|
||||
// in a better-looking animation.
|
||||
duration: Kirigami.Units.veryLongDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
floating: Plasmoid.location === PlasmaCore.Types.Floating
|
||||
removeBorderStrategy: Plasmoid.location === PlasmaCore.Types.Floating
|
||||
? PlasmaCore.AppletPopup.AtScreenEdges
|
||||
: PlasmaCore.AppletPopup.AtScreenEdges | PlasmaCore.AppletPopup.AtPanelEdges
|
||||
|
||||
hideOnWindowDeactivate: root.plasmoidItem.hideOnWindowDeactivate
|
||||
visible: root.plasmoidItem.expanded && root.fullRepresentation
|
||||
visualParent: root.compactRepresentation
|
||||
backgroundHints: (Plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentPrefersOpaqueBackground) ? PlasmaCore.AppletPopup.SolidBackground : PlasmaCore.AppletPopup.StandardBackground
|
||||
appletInterface: root.plasmoidItem
|
||||
|
||||
property var oldStatus: PlasmaCore.Types.UnknownStatus
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
expandedSync.restart();
|
||||
Plasmoid.status = oldStatus;
|
||||
} else {
|
||||
oldStatus = Plasmoid.status;
|
||||
Plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus;
|
||||
// This call currently fails and complains at runtime:
|
||||
// QWindow::setWindowState: QWindow::setWindowState does not accept Qt::WindowActive
|
||||
dialog.requestActivate();
|
||||
}
|
||||
}
|
||||
//It's a MouseEventListener to get all the events, so the eventfilter will be able to catch them
|
||||
mainItem: MouseEventListener {
|
||||
id: appletParent
|
||||
|
||||
focus: true
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
root.plasmoidItem.expanded = false;
|
||||
}
|
||||
|
||||
property real extraWidth: 0
|
||||
property real extraHeight: 0
|
||||
|
||||
Layout.minimumWidth: root.fullRepresentation ? root.fullRepresentation.Layout.minimumWidth + extraWidth : 0
|
||||
Layout.minimumHeight: root.fullRepresentation ? root.fullRepresentation.Layout.minimumHeight + extraHeight : 0
|
||||
|
||||
Layout.maximumWidth: root.fullRepresentation ? root.fullRepresentation.Layout.maximumWidth + extraWidth : Infinity
|
||||
Layout.maximumHeight: root.fullRepresentation ? root.fullRepresentation.Layout.maximumHeight + extraHeight : Infinity
|
||||
|
||||
implicitWidth: {
|
||||
if (root.fullRepresentation !== null) {
|
||||
/****/ if (root.fullRepresentation.Layout.preferredWidth > 0) {
|
||||
return root.fullRepresentation.Layout.preferredWidth + extraWidth;
|
||||
} else if (root.fullRepresentation.implicitWidth > 0) {
|
||||
return root.fullRepresentation.implicitWidth + extraWidth;
|
||||
}
|
||||
}
|
||||
return Kirigami.Units.iconSizes.sizeForLabels * 35;
|
||||
}
|
||||
implicitHeight: {
|
||||
if (root.fullRepresentation !== null) {
|
||||
/****/ if (root.fullRepresentation.Layout.preferredHeight > 0) {
|
||||
return root.fullRepresentation.Layout.preferredHeight + extraHeight;
|
||||
} else if (root.fullRepresentation.implicitHeight > 0) {
|
||||
return root.fullRepresentation.implicitHeight + extraHeight;
|
||||
}
|
||||
}
|
||||
return Kirigami.Units.iconSizes.sizeForLabels * 25;
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus && root.fullRepresentation) {
|
||||
root.fullRepresentation.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
// Draws a line between the applet dialog and the panel
|
||||
KSvg.SvgItem {
|
||||
id: separator
|
||||
// Only draw for popups of panel applets, not desktop applets
|
||||
visible: [PlasmaCore.Types.TopEdge, PlasmaCore.Types.LeftEdge, PlasmaCore.Types.RightEdge, PlasmaCore.Types.BottomEdge]
|
||||
.includes(Plasmoid.location) && !dialog.margin
|
||||
anchors {
|
||||
topMargin: -dialog.topPadding
|
||||
leftMargin: -dialog.leftPadding
|
||||
rightMargin: -dialog.rightPadding
|
||||
bottomMargin: -dialog.bottomPadding
|
||||
}
|
||||
z: 999 /* Draw the line on top of the applet */
|
||||
elementId: (Plasmoid.location === PlasmaCore.Types.TopEdge || Plasmoid.location === PlasmaCore.Types.BottomEdge) ? "horizontal-line" : "vertical-line"
|
||||
imagePath: "widgets/line"
|
||||
states: [
|
||||
State {
|
||||
when: Plasmoid.location === PlasmaCore.Types.TopEdge
|
||||
AnchorChanges {
|
||||
target: separator
|
||||
anchors {
|
||||
top: separator.parent.top
|
||||
left: separator.parent.left
|
||||
right: separator.parent.right
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
separator.height: 1
|
||||
appletParent.extraHeight: 1
|
||||
appletParent.extraWidth: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
when: Plasmoid.location === PlasmaCore.Types.LeftEdge
|
||||
AnchorChanges {
|
||||
target: separator
|
||||
anchors {
|
||||
left: separator.parent.left
|
||||
top: separator.parent.top
|
||||
bottom: separator.parent.bottom
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
separator.width: 1
|
||||
appletParent.extraHeight: 0
|
||||
appletParent.extraWidth: 1
|
||||
}
|
||||
},
|
||||
State {
|
||||
when: Plasmoid.location === PlasmaCore.Types.RightEdge
|
||||
AnchorChanges {
|
||||
target: separator
|
||||
anchors {
|
||||
top: separator.parent.top
|
||||
right: separator.parent.right
|
||||
bottom: separator.parent.bottom
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
separator.width: 1
|
||||
appletParent.extraHeight: 0
|
||||
appletParent.extraWidth: 1
|
||||
}
|
||||
},
|
||||
State {
|
||||
when: Plasmoid.location === PlasmaCore.Types.BottomEdge
|
||||
AnchorChanges {
|
||||
target: separator
|
||||
anchors {
|
||||
left: separator.parent.left
|
||||
right: separator.parent.right
|
||||
bottom: separator.parent.bottom
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
separator.height: 1
|
||||
appletParent.extraHeight: 1
|
||||
appletParent.extraWidth: 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.plasma.workspace.components as WorkspaceComponents
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Kirigami.Icon {
|
||||
id: defaultCompactRepresentation
|
||||
property PlasmoidItem plasmoidItem
|
||||
readonly property bool inPanel: [PlasmaCore.Types.TopEdge, PlasmaCore.Types.RightEdge, PlasmaCore.Types.BottomEdge, PlasmaCore.Types.LeftEdge]
|
||||
.includes(Plasmoid.location)
|
||||
|
||||
Layout.minimumWidth: {
|
||||
switch (Plasmoid.formFactor) {
|
||||
case PlasmaCore.Types.Vertical:
|
||||
return 0;
|
||||
case PlasmaCore.Types.Horizontal:
|
||||
return height;
|
||||
default:
|
||||
return Kirigami.Units.gridUnit * 3;
|
||||
}
|
||||
}
|
||||
|
||||
Layout.minimumHeight: {
|
||||
switch (Plasmoid.formFactor) {
|
||||
case PlasmaCore.Types.Vertical:
|
||||
return width;
|
||||
case PlasmaCore.Types.Horizontal:
|
||||
return 0;
|
||||
default:
|
||||
return Kirigami.Units.gridUnit * 3;
|
||||
}
|
||||
}
|
||||
|
||||
source: Plasmoid.icon || "plasma"
|
||||
active: mouseArea.containsMouse
|
||||
|
||||
activeFocusOnTab: true
|
||||
|
||||
Keys.onPressed: event => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Space:
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Select:
|
||||
Plasmoid.activated();
|
||||
event.accepted = true; // BUG 481393: Prevent system tray from receiving the event
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Accessible.name: Plasmoid.title
|
||||
Accessible.description: plasmoidItem.toolTipSubText ?? ""
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
property bool wasExpanded: false
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onPressed: wasExpanded = defaultCompactRepresentation.plasmoidItem.expanded
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.MiddleButton) {
|
||||
Plasmoid.secondaryActivated();
|
||||
} else {
|
||||
defaultCompactRepresentation.plasmoidItem.expanded = !wasExpanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the FullRepresentation on drag-hover if the applet wants it
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
|
||||
active: defaultCompactRepresentation.plasmoidItem.expandedOnDragHover
|
||||
|
||||
sourceComponent: DropArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onEntered: dropTimer.restart()
|
||||
onExited: dropTimer.stop()
|
||||
|
||||
Timer {
|
||||
id: dropTimer
|
||||
interval: 250 // matches taskmanager delay
|
||||
onTriggered: {
|
||||
defaultCompactRepresentation.plasmoidItem.expanded = true;
|
||||
mouseArea.wasExpanded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: badgeLoader
|
||||
|
||||
anchors.bottom: defaultCompactRepresentation.bottom
|
||||
anchors.right: defaultCompactRepresentation.right
|
||||
|
||||
active: defaultCompactRepresentation.plasmoidItem.badgeText.length != 0
|
||||
|
||||
sourceComponent: WorkspaceComponents.BadgeOverlay {
|
||||
text: defaultCompactRepresentation.plasmoidItem.badgeText
|
||||
icon: defaultCompactRepresentation
|
||||
}
|
||||
|
||||
// Non-default state to center if the badge is wider than the icon
|
||||
states: [
|
||||
State {
|
||||
when: badgeLoader.width >= defaultCompactRepresentation.width
|
||||
AnchorChanges {
|
||||
target: badgeLoader
|
||||
anchors.right: undefined
|
||||
anchors.horizontalCenter: defaultCompactRepresentation.horizontalCenter
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
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 as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kcmutils as KCM
|
||||
|
||||
/**
|
||||
* A copy of Kirigami.AboutPage adapted to KPluginMetadata instead of KAboutData
|
||||
*/
|
||||
KCM.SimpleKCM {
|
||||
id: page
|
||||
title: i18nc("@title:window About this widget", "About")
|
||||
|
||||
property var metaData: Plasmoid.metaData
|
||||
|
||||
Component {
|
||||
id: personDelegate
|
||||
|
||||
RowLayout {
|
||||
height: implicitHeight + (Kirigami.Units.smallSpacing * 2)
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing * 2
|
||||
Kirigami.Icon {
|
||||
implicitWidth: Kirigami.Units.iconSizes.smallMedium
|
||||
implicitHeight: implicitWidth
|
||||
source: "user"
|
||||
}
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: modelData.name
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
Row {
|
||||
// Group action buttons together
|
||||
spacing: 0
|
||||
QQC2.Button {
|
||||
visible: modelData.emailAddress
|
||||
width: height
|
||||
icon.name: "mail-sent"
|
||||
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
text: i18ndc("@action:button icononly for tooltip & accessible", "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.Button {
|
||||
visible: modelData.webAddress
|
||||
width: height
|
||||
icon.name: "globe"
|
||||
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button icononly for tooltip & accessible %1 is 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 {
|
||||
id: column
|
||||
|
||||
readonly property int headingTopSpacing: Kirigami.Units.smallSpacing
|
||||
readonly property int dataLeftSpacing: Kirigami.Units.smallSpacing
|
||||
|
||||
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
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
text: page.metaData.name + " " + page.metaData.version
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 15
|
||||
level: 3
|
||||
type: Kirigami.Heading.Type.Secondary
|
||||
wrapMode: Text.WordWrap
|
||||
text: page.metaData.description
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.topMargin: column.headingTopSpacing
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group", "Website")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
Kirigami.UrlButton {
|
||||
Layout.leftMargin: column.dataLeftSpacing
|
||||
url: page.metaData.website
|
||||
visible: url.length > 0
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.topMargin: column.headingTopSpacing
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group", "Get Help")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
Kirigami.UrlButton {
|
||||
Layout.leftMargin: column.dataLeftSpacing
|
||||
textFormat: Text.PlainText
|
||||
|
||||
url: page.metaData.bugReportUrl
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button urlbutton", "Report an issue")
|
||||
|
||||
visible: page.metaData.bugReportUrl.length > 0
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.topMargin: column.headingTopSpacing
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title license information", "Copyright")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
Layout.leftMargin: column.dataLeftSpacing
|
||||
|
||||
QQC2.Label {
|
||||
text: page.metaData.copyrightText
|
||||
textFormat: Text.PlainText
|
||||
visible: text.length > 0
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@label %1 is the short SPDX text for the license", "License: %1", page.metaData.license)
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
icon.name: "view-readermode"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Read License")
|
||||
onClicked: {
|
||||
licenseComponent.incubateObject(page.Window.window.contentItem, {
|
||||
"text": page.metaData.licenseText,
|
||||
"title": page.metaData.license,
|
||||
}, Qt.Asynchronous);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: column.headingTopSpacing
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group", "Authors")
|
||||
textFormat: Text.PlainText
|
||||
visible: page.metaData.authors.length > 0
|
||||
}
|
||||
Repeater {
|
||||
Layout.leftMargin: column.dataLeftSpacing
|
||||
model: page.metaData.authors
|
||||
delegate: personDelegate
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.topMargin: column.headingTopSpacing
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group", "Credits")
|
||||
textFormat: Text.PlainText
|
||||
visible: repCredits.count > 0
|
||||
}
|
||||
Repeater {
|
||||
id: repCredits
|
||||
Layout.leftMargin: column.dataLeftSpacing
|
||||
model: page.metaData.otherContributors
|
||||
delegate: personDelegate
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.topMargin: column.headingTopSpacing
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group", "Translators")
|
||||
textFormat: Text.PlainText
|
||||
visible: repTranslators.count > 0
|
||||
}
|
||||
Repeater {
|
||||
id: repTranslators
|
||||
Layout.leftMargin: column.dataLeftSpacing
|
||||
model: page.metaData.translators
|
||||
delegate: personDelegate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
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
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kcmutils as KCMUtils
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kitemmodels as KItemModels
|
||||
import org.kde.plasma.configuration
|
||||
import org.kde.plasma.plasmoid
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
implicitWidth: Kirigami.Units.gridUnit * 45
|
||||
implicitHeight: Kirigami.Units.gridUnit * 35
|
||||
|
||||
Layout.minimumWidth: Kirigami.Units.gridUnit * 30
|
||||
Layout.minimumHeight: Kirigami.Units.gridUnit * 21
|
||||
|
||||
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
|
||||
property bool isContainment: false
|
||||
|
||||
property ConfigModel globalConfigModel: globalAppletConfigModel
|
||||
|
||||
property url currentSource
|
||||
|
||||
property bool wasConfigurationChangedSignalSent: false
|
||||
|
||||
function closing() {
|
||||
if (applyButton.enabled) {
|
||||
messageDialog.item = null;
|
||||
messageDialog.open();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function saveConfig() {
|
||||
const config = Plasmoid.configuration; // type: KConfigPropertyMap
|
||||
|
||||
// call applet's own config handling first so it can set cfg_ properties if needed
|
||||
if (app.pageStack.currentItem.saveConfig) {
|
||||
app.pageStack.currentItem.saveConfig()
|
||||
}
|
||||
|
||||
config.keys().forEach(key => {
|
||||
const cfgKey = "cfg_" + key;
|
||||
if (cfgKey in app.pageStack.currentItem) {
|
||||
config[key] = app.pageStack.currentItem[cfgKey];
|
||||
}
|
||||
})
|
||||
|
||||
plasmoid.configuration.writeConfig();
|
||||
}
|
||||
|
||||
function isConfigurationChanged() {
|
||||
const config = Plasmoid.configuration;
|
||||
return config.keys().some(key => {
|
||||
const cfgKey = "cfg_" + key
|
||||
if (!app.pageStack.currentItem.hasOwnProperty(cfgKey))
|
||||
return false
|
||||
return config[key] != app.pageStack.currentItem[cfgKey] &&
|
||||
config[key].toString() != app.pageStack.currentItem[cfgKey].toString()
|
||||
})
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: configDialog
|
||||
function onClosing(event) {
|
||||
event.accepted = closing();
|
||||
}
|
||||
}
|
||||
|
||||
ConfigModel {
|
||||
id: globalAppletConfigModel
|
||||
ConfigCategory {
|
||||
name: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group for configuration dialog page", "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 = wasConfigurationChangedSignalSent || isConfigurationChanged() || (app?.pageStack?.currentItem?.unsavedChanges ?? false);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function open(item) {
|
||||
app.isAboutPage = false;
|
||||
root.currentSource = item.source
|
||||
|
||||
if (item.configUiModule && item.configUiComponent) {
|
||||
root.currentSource = item.configUiModule + item.configUiComponent; // Just for the highlight status
|
||||
const config = Plasmoid.configuration; // type: KConfigPropertyMap
|
||||
|
||||
const props = {
|
||||
"title": item.name,
|
||||
};
|
||||
|
||||
config.keys().forEach(key => {
|
||||
props["cfg_" + key] = config[key];
|
||||
});
|
||||
|
||||
pushReplace(Qt.createComponent(item.configUiModule, item.configUiComponent), props);
|
||||
} else if (item.source) {
|
||||
app.isAboutPage = item.source === Qt.resolvedUrl("AboutPlugin.qml");
|
||||
|
||||
const config = Plasmoid.configuration; // type: KConfigPropertyMap
|
||||
|
||||
const props = { "title": item.name };
|
||||
|
||||
config.keys().forEach(key => {
|
||||
props["cfg_" + key] = config[key];
|
||||
});
|
||||
|
||||
pushReplace(Qt.resolvedUrl(item.source), props);
|
||||
} else {
|
||||
app.pageStack.pop();
|
||||
}
|
||||
|
||||
applyButton.enabled = false
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: app.currentConfigPage
|
||||
ignoreUnknownSignals: true
|
||||
|
||||
// This is an artifact of old internal architecture. If control beyond the automated
|
||||
// monitoring based on cfg_ properties is required, plasmoids should not emit
|
||||
// settingValueChanged() but configurationChanged() to force prompting the user
|
||||
// for saving changes, or use the unsavedChanges property to dynamically indicate
|
||||
// whether saving is needed in the current state).
|
||||
// We keep it around for now as third-party plasmoids might use it (even though they
|
||||
// really shouldn't as it's not documented).
|
||||
// TODO Plasma 7: remove and document in porting guide.
|
||||
function onSettingValueChanged() {
|
||||
wasConfigurationChangedSignalSent = true;
|
||||
}
|
||||
|
||||
function onUnsavedChangesChanged() {
|
||||
root.settingValueChanged()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: app.pageStack
|
||||
|
||||
function onCurrentItemChanged() {
|
||||
if (app.pageStack.currentItem !== null) {
|
||||
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.wasConfigurationChangedSignalSent = true
|
||||
root.settingValueChanged()
|
||||
});
|
||||
}
|
||||
|
||||
const configurationUnsavedChangesSignal = app.pageStack.currentItem.unsavedChangesChanged
|
||||
if (configurationUnsavedChangesSignal) {
|
||||
configurationUnsavedChangesSignal.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 * 7
|
||||
contentWidth: availableWidth
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
activeFocusOnTab: true
|
||||
focus: true
|
||||
Accessible.role: Accessible.PageTabList
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
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: {
|
||||
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: 0
|
||||
width: categoriesScroll.contentWidth
|
||||
focus: true
|
||||
|
||||
function openCategory(item) {
|
||||
if (applyButton.enabled) {
|
||||
messageDialog.item = item;
|
||||
messageDialog.open();
|
||||
return;
|
||||
}
|
||||
open(item)
|
||||
}
|
||||
|
||||
Component {
|
||||
id: categoryDelegate
|
||||
ConfigCategoryDelegate {
|
||||
id: delegate
|
||||
onActivated: categories.openCategory(model);
|
||||
highlighted: {
|
||||
if (app.pageStack.currentItem) {
|
||||
if (model.configUiModule && model.configUiComponent) {
|
||||
return root.currentSource == (model.configUiModule + model.configUiComponent)
|
||||
} 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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group for About dialog page", "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.Auto
|
||||
wideScreen: true
|
||||
pageStack.globalToolBar.separatorVisible: bottomSeparator.visible
|
||||
pageStack.globalToolBar.colorSet: Kirigami.Theme.Window
|
||||
|
||||
property var currentConfigPage: null
|
||||
property bool isAboutPage: false
|
||||
|
||||
Kirigami.PromptDialog {
|
||||
id: messageDialog
|
||||
property var item
|
||||
title: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:window dialog title", "Apply Settings")
|
||||
subtitle: i18ndc("plasma_shell_org.kde.plasma.desktop", "@label dialog body", "The current page has unsaved changes. Apply the changes or discard them?")
|
||||
standardButtons: Kirigami.Dialog.Apply | Kirigami.Dialog.Discard | Kirigami.Dialog.Cancel
|
||||
onApplied: {
|
||||
applyAction.trigger()
|
||||
discarded();
|
||||
}
|
||||
onDiscarded: {
|
||||
wasConfigurationChangedSignalSent = false;
|
||||
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
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
icon.name: "dialog-ok"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "OK")
|
||||
onClicked: acceptAction.trigger()
|
||||
}
|
||||
QQC2.Button {
|
||||
id: applyButton
|
||||
enabled: false
|
||||
icon.name: "dialog-ok-apply"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Apply")
|
||||
visible: !app.isAboutPage && app.pageStack.currentItem
|
||||
onClicked: applyAction.trigger()
|
||||
}
|
||||
QQC2.Button {
|
||||
icon.name: "dialog-cancel"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Cancel")
|
||||
onClicked: cancelAction.trigger()
|
||||
visible: !app.isAboutPage
|
||||
KeyNavigation.tab: categories
|
||||
}
|
||||
}
|
||||
background: Item {
|
||||
Kirigami.Separator {
|
||||
id: bottomSeparator
|
||||
visible: (app.pageStack.currentItem
|
||||
&& app.pageStack.currentItem.flickable
|
||||
&& !(app.pageStack.currentItem.flickable instanceof KCMUtils.GridViewKCM)
|
||||
&& !(app.pageStack.currentItem.flickable.atYBeginning
|
||||
&& app.pageStack.currentItem.flickable.atYEnd)) ?? false
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
id: acceptAction
|
||||
onTriggered: {
|
||||
applyAction.trigger();
|
||||
configDialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
id: applyAction
|
||||
onTriggered: {
|
||||
root.saveConfig()
|
||||
wasConfigurationChangedSignalSent = false
|
||||
applyButton.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
id: cancelAction
|
||||
onTriggered: {
|
||||
if (root.closing()) {
|
||||
configDialog.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: acceptAction.trigger();
|
||||
Keys.onEscapePressed: cancelAction.trigger();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
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
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
QQC2.ItemDelegate {
|
||||
id: delegate
|
||||
|
||||
signal activated()
|
||||
|
||||
//BEGIN properties
|
||||
Layout.fillWidth: true
|
||||
hoverEnabled: true
|
||||
|
||||
Accessible.role: Accessible.PageTab
|
||||
Accessible.name: model.name
|
||||
Accessible.description: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:whatsthis Accessible description for sidebar entries opening configuration page", "Open configuration page")
|
||||
Accessible.onPressAction: delegate.clicked()
|
||||
|
||||
focus: highlighted // need to actually focus highlighted items for the screen reader to see them
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kcmutils as KCM
|
||||
|
||||
KCM.SimpleKCM {
|
||||
id: root
|
||||
|
||||
signal configurationChanged
|
||||
|
||||
property var prettyStrings: {
|
||||
"LeftButton": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Left-Button"),
|
||||
"RightButton": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Right-Button"),
|
||||
"MiddleButton": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Middle-Button"),
|
||||
"BackButton": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Back-Button"),
|
||||
"ForwardButton": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Forward-Button"),
|
||||
|
||||
"wheel:Vertical": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Vertical-Scroll"),
|
||||
"wheel:Horizontal": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Horizontal-Scroll"),
|
||||
|
||||
"ShiftModifier": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Shift"),
|
||||
"ControlModifier": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Ctrl"),
|
||||
"AltModifier": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Alt"),
|
||||
"MetaModifier": i18ndc("plasma_shell_org.kde.plasma.desktop", "@label for shortcuts", "Meta")
|
||||
}
|
||||
|
||||
function saveConfig() {
|
||||
configDialog.currentContainmentActionsModel.save();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: configDialog.currentContainmentActionsModel
|
||||
function onConfigurationChanged() {
|
||||
root.configurationChanged()
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: mainColumn
|
||||
flow: GridLayout.TopToBottom
|
||||
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 root.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"
|
||||
valueRole: "pluginName"
|
||||
property bool initialized: false
|
||||
Component.onCompleted: {
|
||||
currentIndex = indexOfValue(pluginName)
|
||||
pluginsCombo.initialized = true;
|
||||
}
|
||||
onActivated: {
|
||||
if (initialized) {
|
||||
var newPluginName = currentValue;
|
||||
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"
|
||||
enabled: model.hasConfigurationInterface
|
||||
onClicked: {
|
||||
configDialog.currentContainmentActionsModel.showConfiguration(index, this);
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
icon.name: "list-remove"
|
||||
onClicked: {
|
||||
configDialog.currentContainmentActionsModel.remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseEventInputButton {
|
||||
defaultText: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Add Action");
|
||||
icon.name: checked ? "input-mouse-symbolic" : "list-add"
|
||||
onEventStringChanged: {
|
||||
configDialog.currentContainmentActionsModel.append(eventString, "org.kde.contextmenu");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import QtQml
|
||||
|
||||
import org.kde.newstuff as NewStuff
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kcmutils
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.plasma.configuration
|
||||
|
||||
SimpleKCM {
|
||||
id: appearanceRoot
|
||||
signal configurationChanged
|
||||
|
||||
leftPadding: 0 // let wallpaper config touch the edges in case there's a List/GridView'
|
||||
rightPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
property int formAlignment: wallpaperComboBox.Kirigami.ScenePosition.x - appearanceRoot.Kirigami.ScenePosition.x + Kirigami.Units.smallSpacing
|
||||
property string originalWallpaper: ""
|
||||
property alias parentLayout: parentLayout
|
||||
property bool unsavedChanges: false
|
||||
|
||||
function saveConfig() {
|
||||
if (main.currentItem.saveConfig) {
|
||||
main.currentItem.saveConfig()
|
||||
}
|
||||
configDialog.currentWallpaper = wallpaperComboBox.currentValue
|
||||
appearanceRoot.originalWallpaper = wallpaperComboBox.currentValue
|
||||
configDialog.wallpaperConfiguration.keys().forEach(key => {
|
||||
if (main.currentItem["cfg_"+key] !== undefined) {
|
||||
configDialog.wallpaperConfiguration[key] = main.currentItem["cfg_"+key]
|
||||
}
|
||||
})
|
||||
configDialog.applyWallpaper()
|
||||
configDialog.containmentPlugin = pluginComboBox.currentValue
|
||||
appearanceRoot.closeContainmentWarning()
|
||||
appearanceRoot.unsavedChanges = false
|
||||
}
|
||||
|
||||
function checkUnsavedChanges() {
|
||||
const wallpaperConfig = configDialog.wallpaperConfiguration
|
||||
appearanceRoot.unsavedChanges = configDialog.currentWallpaper != appearanceRoot.originalWallpaper ||
|
||||
configDialog.containmentPlugin != pluginComboBox.currentValue ||
|
||||
wallpaperConfig.keys().some(key => {
|
||||
const cfgKey = "cfg_" + key
|
||||
if (!(cfgKey in main.currentItem) || key.startsWith("PreviewImage") || key.endsWith("Default")) {
|
||||
return false
|
||||
}
|
||||
return main.currentItem[cfgKey] != wallpaperConfig[key] &&
|
||||
main.currentItem[cfgKey].toString() != wallpaperConfig[key].toString()
|
||||
})
|
||||
}
|
||||
|
||||
function closeContainmentWarning() {
|
||||
if (main.currentItem?.objectName === "switchContainmentWarningItem") {
|
||||
main.pop();
|
||||
categoriesScroll.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: appearanceRoot.availableWidth
|
||||
height: Math.max(implicitHeight, appearanceRoot.availableHeight)
|
||||
spacing: 0 // unless it's 0 there will be an additional gap between two FormLayouts
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
visible: Plasmoid.immutable || animating
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:status inlinemessage", "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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@label:listbox", "Layout:")
|
||||
enabled: !Plasmoid.immutable
|
||||
model: configDialog.containmentPluginsConfigModel
|
||||
textRole: "name"
|
||||
valueRole: "pluginName"
|
||||
onActivated: {
|
||||
if (configDialog.containmentPlugin !== pluginComboBox.currentValue) {
|
||||
main.push(switchContainmentWarning);
|
||||
categoriesScroll.enabled = false;
|
||||
} else {
|
||||
closeContainmentWarning()
|
||||
}
|
||||
appearanceRoot.checkUnsavedChanges()
|
||||
}
|
||||
Component.onCompleted: {
|
||||
currentIndex = indexOfValue(configDialog.containmentPlugin)
|
||||
activated(currentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
enabled: main.currentItem.objectName !== "switchContainmentWarningItem"
|
||||
Kirigami.FormData.label: i18ndc("plasma_shell_org.kde.plasma.desktop", "@label:listbox", "Wallpaper type:")
|
||||
Kirigami.FormData.buddyFor: wallpaperComboBox
|
||||
|
||||
QQC2.ComboBox {
|
||||
id: wallpaperComboBox
|
||||
|
||||
function selectCurrentWallpaperPlugin() {
|
||||
currentIndex = indexOfValue(configDialog.currentWallpaper)
|
||||
appearanceRoot.originalWallpaper = currentValue
|
||||
activated(currentIndex)
|
||||
}
|
||||
|
||||
Layout.preferredWidth: Math.max(implicitWidth, pluginComboBox.implicitWidth)
|
||||
model: configDialog.wallpaperConfigModel
|
||||
textRole: "name"
|
||||
valueRole: "pluginName"
|
||||
onActivated: {
|
||||
var idx = configDialog.wallpaperConfigModel.index(currentIndex, 0)
|
||||
if (configDialog.currentWallpaper === currentValue && main.sourceFile !== "tbd") {
|
||||
return;
|
||||
}
|
||||
configDialog.currentWallpaper = currentValue
|
||||
main.sourceFile = idx.data(ConfigModel.SourceRole)
|
||||
appearanceRoot.checkUnsavedChanges()
|
||||
}
|
||||
Component.onCompleted: {
|
||||
selectCurrentWallpaperPlugin();
|
||||
}
|
||||
|
||||
Connections {
|
||||
enabled: true
|
||||
target: configDialog.wallpaperConfigModel
|
||||
function onWallpaperPluginsChanged() {
|
||||
wallpaperComboBox.selectCurrentWallpaperPlugin();
|
||||
}
|
||||
}
|
||||
}
|
||||
NewStuff.Button {
|
||||
configFile: "wallpaperplugin.knsrc"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "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,
|
||||
"wallpaperConfiguration": Qt.binding(() => Plasmoid.wallpaperGraphicsObject.configuration)
|
||||
}
|
||||
|
||||
// Some third-party wallpaper plugins need the config keys to be set initially.
|
||||
// We should not break them within one Plasma major version, but setting everything
|
||||
// will lead to an error message for every unused property (and some, like KConfigXT
|
||||
// default values, are used by almost no plugin configuration). We load the config
|
||||
// page in a temp variable first, then use that to figure out which ones we need to
|
||||
// set initially.
|
||||
// TODO Plasma 7: consider whether we can drop this workaround
|
||||
const temp = Qt.createComponent(Qt.resolvedUrl(sourceFile)).createObject(appearanceRoot, props)
|
||||
wallpaperConfig.keys().forEach(key => {
|
||||
const cfgKey = "cfg_" + key;
|
||||
if (cfgKey in temp) {
|
||||
props[cfgKey] = wallpaperConfig[key]
|
||||
}
|
||||
})
|
||||
temp.destroy()
|
||||
|
||||
var newItem = replace(Qt.resolvedUrl(sourceFile), props)
|
||||
|
||||
wallpaperConfig.keys().forEach(key => {
|
||||
const cfgKey = "cfg_" + key;
|
||||
if (cfgKey in newItem) {
|
||||
let changedSignal = main.currentItem[cfgKey + "Changed"]
|
||||
if (changedSignal) {
|
||||
changedSignal.connect(appearanceRoot.checkUnsavedChanges)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info", "Layout changes must be applied before other changes can be made")
|
||||
|
||||
helpfulAction: QQC2.Action {
|
||||
icon.name: "dialog-ok-apply"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Apply Now")
|
||||
onTriggered: appearanceRoot.saveConfig()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kquickcontrols
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kcmutils as KCM
|
||||
|
||||
KCM.SimpleKCM {
|
||||
id: root
|
||||
|
||||
title: i18nc("@title:window for configuration page", "Shortcuts")
|
||||
|
||||
property bool unsavedChanges: false
|
||||
|
||||
function saveConfig() {
|
||||
Plasmoid.globalShortcut = button.keySequence
|
||||
unsavedChanges = false
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
|
||||
KeySequenceItem {
|
||||
id: button
|
||||
Kirigami.FormData.label: i18nc("@action:button set keyboard shortcut for", "Activate widget as if clicked:")
|
||||
keySequence: Plasmoid.globalShortcut
|
||||
patterns: ShortcutPattern.Modifier | ShortcutPattern.ModifierAndKey
|
||||
onKeySequenceModified: root.unsavedChanges = keySequence !== Plasmoid.globalShortcut
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.configuration
|
||||
|
||||
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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group for configuration dialog page", "Wallpaper")
|
||||
icon: "preferences-desktop-wallpaper"
|
||||
source: "ConfigurationContainmentAppearance.qml"
|
||||
}
|
||||
ConfigCategory {
|
||||
name: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group for configuration dialog page", "Mouse Actions")
|
||||
icon: "preferences-desktop-mouse"
|
||||
source: "ConfigurationContainmentActions.qml"
|
||||
}
|
||||
}
|
||||
//END model
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
|
||||
|
||||
QQC2.Button {
|
||||
id: mouseInputButton
|
||||
property string defaultText: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button inactive state for button recording mouse input" ,"Add Action")
|
||||
text: defaultText
|
||||
checkable: true
|
||||
property string eventString
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
text = i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button active state for button recording mouse input", "Input Here");
|
||||
mouseInputArea.enabled = true;
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: mouseInputArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.AllButtons
|
||||
enabled: false
|
||||
|
||||
onClicked: mouse => {
|
||||
var newEventString = configDialog.currentContainmentActionsModel.mouseEventString(mouse.button, mouse.modifiers);
|
||||
|
||||
if (mouseInputButton.eventString === newEventString || !configDialog.currentContainmentActionsModel.isTriggerUsed(newEventString)) {
|
||||
if (mouseInputButton.eventString === newEventString) {
|
||||
// fire changed signal so deleted button can return if needed
|
||||
mouseInputButton.eventStringChanged()
|
||||
} else {
|
||||
mouseInputButton.eventString = newEventString;
|
||||
}
|
||||
mouseInputButton.text = mouseInputButton.defaultText;
|
||||
mouseInputButton.checked = false;
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
onWheel: wheel => {
|
||||
var newEventString = configDialog.currentContainmentActionsModel.wheelEventString(wheel);
|
||||
|
||||
if (mouseInputButton.eventString === newEventString || !configDialog.currentContainmentActionsModel.isTriggerUsed(newEventString)) {
|
||||
mouseInputButton.eventString = newEventString;
|
||||
mouseInputButton.text = mouseInputButton.defaultText;
|
||||
mouseInputButton.checked = false;
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,762 @@
|
||||
/*
|
||||
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 as QQC2
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.components as PC3
|
||||
import org.kde.plasma.shell.panel as Panel
|
||||
import org.kde.kquickcontrols
|
||||
import "panelconfiguration"
|
||||
|
||||
ColumnLayout {
|
||||
id: dialogRoot
|
||||
spacing: Kirigami.Units.largeSpacing * 2
|
||||
|
||||
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:window", "Panel Settings")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
PC3.ToolButton {
|
||||
id: addNewButton
|
||||
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button label of button to open a menu that lets you add widgets or spacers", "Add New")
|
||||
icon.name: "list-add-symbolic"
|
||||
down: addMenu.opened
|
||||
Accessible.role: Accessible.ButtonMenu
|
||||
|
||||
onClicked: {
|
||||
if (addMenu.visible) {
|
||||
addMenu.dismiss();
|
||||
} else {
|
||||
addMenu.open();
|
||||
}
|
||||
}
|
||||
|
||||
PC3.Menu {
|
||||
id: addMenu
|
||||
y: addNewButton.height
|
||||
|
||||
PC3.MenuItem {
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:menu add a spacer", "Spacer")
|
||||
icon.name: "distribute-horizontal-x"
|
||||
|
||||
onClicked: configDialog.addPanelSpacer()
|
||||
}
|
||||
|
||||
PC3.MenuItem {
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:menu add a widget", "Widgets…")
|
||||
icon.name: "view-group-symbolic"
|
||||
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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group", "Position")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
PanelRepresentation {
|
||||
id: positionRepresentation
|
||||
text: (panel.location === PlasmaCore.Types.TopEdge ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@label panel screen edge", "Top") :
|
||||
panel.location === PlasmaCore.Types.RightEdge ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@label panel screen edge", "Right") :
|
||||
panel.location === PlasmaCore.Types.LeftEdge ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@label panel screen edge", "Left") :
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@label panel screen edge", "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: i18ndc("plasma_shell_org.kde.plasma.desktop", "action:button", "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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group", "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 ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Top") : i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Left"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Center"),
|
||||
dialogRoot.vertical ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Bottom") : i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "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
|
||||
}
|
||||
}
|
||||
}
|
||||
PC3.ComboBox {
|
||||
model: [alignmentBox.model.reduce((a, b) => a.length > b.length ? a : b)]
|
||||
|
||||
Component.onCompleted: {
|
||||
parent.Layout.minimumWidth = implicitWidth
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Kirigami.Units.mediumSpacing
|
||||
Kirigami.Heading {
|
||||
level: dialogRoot.headingLevel
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: dialogRoot.vertical ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group panel height", "Height")
|
||||
: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group panel width", "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 ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox panel fills the full height of the display", "Fill height") : i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox panel fills the full width of the display", "Fill width"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox panel is just big enough to fit its content", "Fit content"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox panel size", "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
|
||||
}
|
||||
}
|
||||
}
|
||||
PC3.ComboBox {
|
||||
model: [widthBox.model.reduce((a, b) => a.length > b.length ? a : b)]
|
||||
|
||||
Component.onCompleted: {
|
||||
parent.Layout.minimumWidth = implicitWidth
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Kirigami.Units.mediumSpacing
|
||||
Kirigami.Heading {
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
level: dialogRoot.headingLevel
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group", "Visibility")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
PanelRepresentation {
|
||||
id: visibilityRepresentation
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
sunkenPanel: autoHideBox.previewIndex == 1 || autoHideBox.previewIndex == 2
|
||||
onClicked: autoHideBox.popup.visible = true
|
||||
windowVisible: true
|
||||
panelReservesSpace: autoHideBox.previewIndex == 0
|
||||
isVertical: dialogRoot.vertical
|
||||
alignment: positionRepresentation.alignment
|
||||
}
|
||||
PC3.ComboBox {
|
||||
id: autoHideBox
|
||||
property int previewIndex: popup.visible ? highlightedIndex : currentIndex
|
||||
property int animationIndex: popup.visible ? highlightedIndex : -1
|
||||
model: [
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Always visible"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Auto hide"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Dodge windows"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Windows go below"),
|
||||
]
|
||||
onAnimationIndexChanged: {
|
||||
if (animationIndex == 0 || animationIndex == 3) {
|
||||
visibilityRepresentation.maximizeWindow()
|
||||
} else if (animationIndex == 1) {
|
||||
visibilityRepresentation.hidePanel()
|
||||
} else if (animationIndex == 2) {
|
||||
visibilityRepresentation.dodgePanel()
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
PC3.ComboBox {
|
||||
model: [autoHideBox.model.reduce((a, b) => a.length > b.length ? a : b)]
|
||||
|
||||
Component.onCompleted: {
|
||||
parent.Layout.minimumWidth = implicitWidth
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Kirigami.Units.mediumSpacing
|
||||
Kirigami.Heading {
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
level: dialogRoot.headingLevel
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group", "Opacity")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
PanelRepresentation {
|
||||
id: opacityRepresentation
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
adaptivePanel: transparencyBox.previewIndex === 0
|
||||
translucentPanel: transparencyBox.previewIndex === 2
|
||||
onClicked: transparencyBox.popup.visible = true
|
||||
isVertical: dialogRoot.vertical
|
||||
alignment: positionRepresentation.alignment
|
||||
}
|
||||
PC3.ComboBox {
|
||||
id: transparencyBox
|
||||
readonly property int previewIndex: popup.visible ? highlightedIndex : currentIndex
|
||||
model: [
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Adaptive"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "Opaque"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox", "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
|
||||
}
|
||||
}
|
||||
}
|
||||
PC3.ComboBox {
|
||||
model: [transparencyBox.model.reduce((a, b) => a.length > b.length ? a : b)]
|
||||
|
||||
Component.onCompleted: {
|
||||
parent.Layout.minimumWidth = implicitWidth
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Kirigami.Units.mediumSpacing
|
||||
Kirigami.Heading {
|
||||
level: dialogRoot.headingLevel
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop","@title:group Degree of floatingness of panel and applets", "Floating")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
PanelRepresentation {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
floatingGap: Kirigami.Units.smallSpacing * (floatingBox.previewIndex === 2)
|
||||
onClicked: floatingBox.popup.visible = true
|
||||
visibleApplet: true
|
||||
floatingApplet: floatingBox.previewIndex !== 0
|
||||
isVertical: dialogRoot.vertical
|
||||
alignment: positionRepresentation.alignment
|
||||
}
|
||||
PC3.ComboBox {
|
||||
id: floatingBox
|
||||
readonly property int previewIndex: popup.visible ? highlightedIndex : currentIndex
|
||||
model: [
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox Option to disable floating panels or applets", "Disabled"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox Option to make only panel applets always float", "Applets only"),
|
||||
i18ndc("plasma_shell_org.kde.plasma.desktop", "@item:inlistbox Option to make panel and applets floating", "Panel and applets")
|
||||
]
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.minimumHeight: transparencyBox.height
|
||||
Layout.minimumWidth: opacityRepresentation.width
|
||||
currentIndex: (panel.floating ? 2 : panel.floatingApplets ? 1 : 0)
|
||||
onActivated: (index) => {
|
||||
if (index === 0) {
|
||||
panel.floating = panel.floatingApplets = false
|
||||
} else if (index === 1) {
|
||||
panel.floating = false
|
||||
panel.floatingApplets = true
|
||||
} else {
|
||||
panel.floating = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: 3
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
visible: panel.unsupportedConfiguration
|
||||
|
||||
Kirigami.Icon {
|
||||
source: "data-warning-symbolic"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
|
||||
}
|
||||
|
||||
PC3.Label {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
// Popup doesn't have to expand to the implicit size of this label, let it wrap.
|
||||
Layout.preferredWidth: 0
|
||||
text: panel.unsupportedConfigurationDescription
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
PC3.ToolButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Revert an unsupported configuration back to the defaults", "Fix it")
|
||||
icon.name: "tools-wizard-symbolic"
|
||||
onClicked: {
|
||||
panel.fixUnsupportedConfiguration();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
id: dialogInstantiator
|
||||
active: setPositionButton.checked || clonePanelButton.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: dialogInstantiator.active && (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: {
|
||||
if (setPositionButton.checked) {
|
||||
setPositionButton.moveTo(root.onClickedLocation, Window.window)
|
||||
} else if (clonePanelButton.checked) {
|
||||
panel.clonePanelTo(root.onClickedLocation, dialogRoot.panelConfiguration.screenFromWindow(Window.window))
|
||||
clonePanelButton.checked = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
? i18ndc("plasma_shell_org.kde.plasma.desktop", "@label:spinbox", "Panel Width:")
|
||||
: i18ndc("plasma_shell_org.kde.plasma.desktop", "@label:spinbox", "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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group shortcut that moves focus to the panel", "Focus shortcut:")
|
||||
textFormat: Text.PlainText
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
PC3.ToolTip {
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip", "Press this keyboard shortcut to move focus to the Panel")
|
||||
visible: mouseArea.containsMouse
|
||||
}
|
||||
}
|
||||
KeySequenceItem {
|
||||
id: button
|
||||
|
||||
Accessible.name: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button accessible name", "Focus Shortcut Setter")
|
||||
Accessible.description: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:whatsthis Accessible description for button", "Button to set the shortcut for the panel to gain focus")
|
||||
Accessible.onPressAction: startCapturing()
|
||||
|
||||
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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip for button", "Remove this panel; this action can be undone")
|
||||
PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
PC3.ToolTip.visible: hovered
|
||||
|
||||
onClicked: plasmoid.internalAction("remove").trigger()
|
||||
}
|
||||
|
||||
PC3.ToolButton {
|
||||
id: clonePanelButton
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Clone the panel", "Clone Panel")
|
||||
icon.name: "edit-copy-symbolic"
|
||||
checkable: true
|
||||
|
||||
PC3.ToolTip.text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip for button", "Create a new panel with the same settings and applets")
|
||||
PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
PC3.ToolTip.visible: hovered
|
||||
}
|
||||
|
||||
|
||||
Item {Layout.fillWidth: true}
|
||||
|
||||
PC3.ToolButton {
|
||||
text: plasmoid.corona.enteredEditModeViaDesktop() ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Close the panel configuration window", "Close") : i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Close the panel configuration window and exit edit mode", "Exit Edit Mode")
|
||||
icon.name: "dialog-ok-symbolic"
|
||||
|
||||
PC3.ToolTip.text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip for button", "Close Panel Settings window and exit Edit Mode")
|
||||
PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
PC3.ToolTip.visible: hovered
|
||||
|
||||
onClicked: plasmoid.internalAction("configure").trigger()
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021 Cyril Rossi <cyril.rossi@enioka.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import "shellcontainmentconfiguration"
|
||||
|
||||
Kirigami.AbstractApplicationWindow {
|
||||
id: root
|
||||
|
||||
title: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:window", "Manage Panels and Desktops")
|
||||
|
||||
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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:usagetip", "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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Close window", "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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.components as PC3
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
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 isHorizontal: !isVertical
|
||||
property bool checked: false
|
||||
property bool windowVisible: false
|
||||
property bool panelVisible: true
|
||||
|
||||
property bool isTop: !!(alignment & Qt.AlignTop)
|
||||
property bool isBottom: !!(alignment & Qt.AlignBottom)
|
||||
property bool isLeft: !!(alignment & Qt.AlignLeft)
|
||||
property bool isRight: !!(alignment & Qt.AlignRight)
|
||||
|
||||
property bool translucentPanel: false
|
||||
property bool sunkenPanel: false
|
||||
property bool adaptivePanel: false
|
||||
property bool panelReservesSpace: true
|
||||
|
||||
property bool fillAvailable: false
|
||||
property int floatingGap: 0
|
||||
property var mainIconSource: null
|
||||
property int screenHeight: Math.round(screenRect.height / 2)
|
||||
|
||||
property bool visibleApplet: false
|
||||
property bool floatingApplet: false
|
||||
|
||||
readonly property bool iconAndLabelsShouldlookSelected: checked || mouseArea.pressed
|
||||
|
||||
// Layouts automatically mirror on RTL languages, and we use
|
||||
// layouts to position the panel here. As a result, RTL languages
|
||||
// show the panel on the opposite side, which does not match
|
||||
// where the panel actually is. We instead disable layout
|
||||
// mirroring to avoid that.
|
||||
LayoutMirroring.enabled: false
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
function maximizeWindow() {
|
||||
|
||||
hidePanelLater.stop()
|
||||
hidePanel.stop()
|
||||
showPanel.restart()
|
||||
|
||||
moveWindowOverPanel.stop()
|
||||
resetWindowOverPanel.restart()
|
||||
|
||||
maximizeAnimation.restart()
|
||||
}
|
||||
function hidePanel() {
|
||||
|
||||
hidePanelLater.stop()
|
||||
maximizeAnimation.stop()
|
||||
resetMaximize.restart()
|
||||
|
||||
moveWindowOverPanel.stop()
|
||||
resetWindowOverPanel.restart()
|
||||
|
||||
hidePanel.restart()
|
||||
}
|
||||
|
||||
function dodgePanel() {
|
||||
|
||||
hidePanel.stop()
|
||||
showPanel.restart()
|
||||
|
||||
maximizeAnimation.stop()
|
||||
resetMaximize.restart()
|
||||
|
||||
moveWindowOverPanel.restart()
|
||||
hidePanelLater.restart()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hidePanelLater
|
||||
interval: 200
|
||||
onTriggered: hidePanel.restart()
|
||||
running: false
|
||||
}
|
||||
|
||||
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
|
||||
z: 1
|
||||
|
||||
Rectangle {
|
||||
id: panelImage
|
||||
|
||||
property real sunkenValue: 0
|
||||
|
||||
Component.onCompleted: {
|
||||
panelImage.sunkenValue = root.sunkenPanel / 2
|
||||
}
|
||||
|
||||
implicitWidth: root.isVertical ? Math.round(parent.width / 6) : Math.round(parent.width * (root.fillAvailable ? 1 : 0.7))
|
||||
implicitHeight: root.isVertical ? Math.round(parent.height * (root.fillAvailable ? 1 : 0.8)) : Math.round(parent.height / 4)
|
||||
|
||||
Layout.alignment: root.alignment
|
||||
Layout.bottomMargin: root.isBottom ? sunkenValue * -Math.round(height) + root.floatingGap : 0
|
||||
Layout.topMargin: root.isTop ? sunkenValue * -Math.round(height) + root.floatingGap : 0
|
||||
Layout.leftMargin: root.isLeft ? sunkenValue * -Math.round(width) + root.floatingGap : 0
|
||||
Layout.rightMargin: root.isRight ? sunkenValue * -Math.round(width) + root.floatingGap : 0
|
||||
|
||||
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
|
||||
|
||||
SequentialAnimation on sunkenValue {
|
||||
id: hidePanel
|
||||
running: false
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 1
|
||||
duration: Kirigami.Units.veryLongDuration
|
||||
}
|
||||
PauseAnimation {
|
||||
duration: Kirigami.Units.veryLongDuration * 2
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 0.5
|
||||
duration: Kirigami.Units.veryLongDuration
|
||||
}
|
||||
}
|
||||
SequentialAnimation on sunkenValue {
|
||||
id: showPanel
|
||||
running: false
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: horizontalAdaptivePanelLoader
|
||||
active: root.adaptivePanel && root.isHorizontal
|
||||
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 {
|
||||
x: panelImage.x + root.isLeft * panelImage.width - root.isRight * width + root.isVertical * panelSpacing
|
||||
y: panelImage.y + root.isTop * panelImage.height - root.isBottom * height + (root.isHorizontal) * panelSpacing
|
||||
height: Math.round(parent.height / 2)
|
||||
width: Math.round(parent.width / 3)
|
||||
visible: root.visibleApplet
|
||||
|
||||
property int panelSpacing: Kirigami.Units.smallSpacing * (root.floatingApplet ? -1 : 1) * (root.isRight || root.isBottom ? 1 : -1)
|
||||
|
||||
color: window.color
|
||||
radius: 5
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
}
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: window
|
||||
|
||||
property real maximized: 0
|
||||
property real windowOverPanel: 0
|
||||
|
||||
width: Math.round(parent.width * (0.4 + 0.6 * maximized) - panelImage.width * root.panelReservesSpace * maximized * root.isVertical)
|
||||
height: Math.round(parent.height * (0.4 + 0.6 * maximized) - panelImage.height * root.panelReservesSpace * maximized * root.isHorizontal)
|
||||
visible: root.windowVisible
|
||||
radius: 5
|
||||
color: Kirigami.Theme.highlightColor
|
||||
border.color: "transparent"
|
||||
|
||||
x: Math.round(screenRect.width / 2 - width / 2) * (1 - maximized) + windowOverPanel * Kirigami.Units.mediumSpacing * 2 * root.isVertical * (root.isLeft ? - 1 : 1) + panelImage.width * maximized * root.isLeft * root.panelReservesSpace
|
||||
y: Math.round(screenRect.height / 2 - height / 2) * (1 - maximized) + windowOverPanel * Kirigami.Units.mediumSpacing * 2 * root.isHorizontal * (root.isTop ? - 1 : 1) + panelImage.height * maximized * root.isTop * root.panelReservesSpace
|
||||
z: 0
|
||||
|
||||
SequentialAnimation on maximized {
|
||||
id: maximizeAnimation
|
||||
running: false
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 1
|
||||
duration: Kirigami.Units.longDuration
|
||||
}
|
||||
PauseAnimation {
|
||||
duration: Kirigami.Units.veryLongDuration * 2
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: Kirigami.Units.longDuration
|
||||
}
|
||||
}
|
||||
NumberAnimation on maximized {
|
||||
id: resetMaximize
|
||||
running: false
|
||||
to: 0
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
SequentialAnimation on windowOverPanel {
|
||||
id: moveWindowOverPanel
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 1
|
||||
duration: Kirigami.Units.veryLongDuration
|
||||
}
|
||||
PauseAnimation {
|
||||
duration: Kirigami.Units.veryLongDuration * 2
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 0
|
||||
duration: Kirigami.Units.veryLongDuration
|
||||
}
|
||||
}
|
||||
NumberAnimation on windowOverPanel {
|
||||
id: resetWindowOverPanel
|
||||
running: false
|
||||
to: 0
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.ksvg as KSvg
|
||||
import org.kde.plasma.shell.panel as Panel
|
||||
|
||||
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
|
||||
? i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip slider handle", "Drag to change maximum height.")
|
||||
: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip slider handle", "Drag to change maximum width.")
|
||||
) + "\n" + i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip slider handle", "Double-click to reset.")
|
||||
property string minimumText: (
|
||||
dialogRoot.vertical
|
||||
? i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip slider handle", "Drag to change minimum height.")
|
||||
: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip slider handle", "Drag to change minimum width.")
|
||||
) + "\n" + i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip slider handle", "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: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:tooltip slider handle",
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.ksvg as KSvg
|
||||
import org.kde.plasma.components as PC3
|
||||
import org.kde.plasma.shell.panel as Panel
|
||||
|
||||
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) {
|
||||
let newY = 0
|
||||
if (alignment === Qt.AlignRight) {
|
||||
newY = root.parent.height - (value + offset + root.height/2)
|
||||
} else if (alignment === Qt.AlignLeft) {
|
||||
newY = value + offset - root.height/2
|
||||
} else {
|
||||
if (root.alignment & Qt.AlignRight) {
|
||||
newY = root.parent.height/2 - value/2 + offset - root.height/2
|
||||
} else if (root.alignment & Qt.AlignLeft) {
|
||||
newY = root.parent.height/2 + value/2 + offset - root.height/2
|
||||
} else {
|
||||
newY = root.parent.height/2 + value + offset -root.height/2
|
||||
}
|
||||
}
|
||||
y = Math.max(-height/2, Math.min(parent.height - height/2, newY))
|
||||
} else {
|
||||
let newX = 0
|
||||
if (alignment === Qt.AlignRight) {
|
||||
newX = root.parent.width - (value + offset + root.width/2)
|
||||
} else if (alignment === Qt.AlignLeft) {
|
||||
newX = value + offset - root.width/2
|
||||
} else {
|
||||
if (root.alignment & Qt.AlignRight) {
|
||||
newX = root.parent.width/2 - value/2 + offset - root.width/2
|
||||
} else if (root.alignment & Qt.AlignLeft) {
|
||||
newX = root.parent.width/2 + value/2 + offset -root.width/2
|
||||
} else {
|
||||
newX = root.parent.width/2 + value + offset -root.width/2
|
||||
}
|
||||
}
|
||||
x = Math.max(-width/2, Math.min(parent.width - width/2, newX))
|
||||
}
|
||||
}
|
||||
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 + root.offset + root.height/2)
|
||||
} else if (root.alignment === Qt.AlignLeft) {
|
||||
root.value = root.y - root.offset + root.height/2
|
||||
//Center
|
||||
} else {
|
||||
if (root.alignment & Qt.AlignRight) {
|
||||
root.value = (root.parent.height/2 - root.y + root.offset)*2 - root.height
|
||||
} else if (root.alignment & Qt.AlignLeft) {
|
||||
root.value = (root.y - root.offset - root.parent.height/2)*2 + root.height
|
||||
} else {
|
||||
var value = root.y - root.parent.height/2 - root.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 + root.offset + root.width/2)
|
||||
} else if (root.alignment === Qt.AlignLeft) {
|
||||
root.value = root.x - root.offset + root.width/2
|
||||
//Center
|
||||
} else {
|
||||
if (root.alignment & Qt.AlignRight) {
|
||||
root.value = (root.parent.width/2 - root.x + root.offset)*2 - root.width
|
||||
} else if (root.alignment & Qt.AlignLeft) {
|
||||
root.value = (root.x - root.offset - root.parent.width/2)*2 + root.width
|
||||
} else {
|
||||
var value = root.x - root.parent.width/2 - root.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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
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
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami 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"
|
||||
visible: contextMenuRepeater.anyActionAvailable || removeItem.itemVisible
|
||||
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 {
|
||||
id: contextMenuRepeater
|
||||
model: ShellContainmentModel
|
||||
|
||||
// There will always be at least one hidden action for the
|
||||
// current screen; if there's any other, then we should
|
||||
// display them.
|
||||
property bool anyActionAvailable: count > 1
|
||||
|
||||
QQC2.MenuItem {
|
||||
text: edge == "floating"
|
||||
? i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:inmenu", "Swap with Desktop on Screen %1", model.screenName)
|
||||
: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:inmenu", "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 && contextMenuRepeater.anyActionAvailable
|
||||
}
|
||||
QQC2.MenuItem {
|
||||
id: removeItem
|
||||
text: contRect.state === "floating"
|
||||
? i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:inmenu", "Remove Desktop")
|
||||
: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:inmenu", "Remove Panel")
|
||||
icon.name: "edit-delete"
|
||||
onTriggered: {
|
||||
if (contRect.state === "floating") {
|
||||
ShellContainmentModel.remove(screenId);
|
||||
} else {
|
||||
containments.remove(containmentId);
|
||||
}
|
||||
}
|
||||
// We read this variable elsewhere to know if this item
|
||||
// would be visible if the context menu were to be shown.
|
||||
property bool itemVisible: contRect.state !== "floating" || !model.active
|
||||
visible: itemVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
? i18ndc("plasma_shell_org.kde.plasma.desktop", "@info % is screen name", "%1 (primary)", model.screenName)
|
||||
: model.screenName
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[Desktop]
|
||||
Containment=org.kde.plasma.folder
|
||||
ToolBox=
|
||||
RuntimePlatform=Desktop
|
||||
|
||||
[Desktop][ContainmentActions]
|
||||
RightButton;NoModifier=org.kde.contextmenu
|
||||
MiddleButton;NoModifier=org.kde.paste
|
||||
|
||||
[Panel]
|
||||
Containment=org.kde.panel
|
||||
ToolBox=org.kde.paneltoolbox
|
||||
[Panel][ContainmentActions]
|
||||
RightButton;NoModifier=org.kde.contextmenu
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.components as PlasmaComponents3
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.plasma.private.shell
|
||||
import org.kde.plasma.shell
|
||||
|
||||
PlasmaCore.Dialog {
|
||||
id: dialog
|
||||
|
||||
required property AlternativesHelper alternativesHelper
|
||||
|
||||
visualParent: alternativesHelper.applet
|
||||
location: alternativesHelper.applet.Plasmoid.location
|
||||
hideOnWindowDeactivate: true
|
||||
backgroundHints: (alternativesHelper.applet.Plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentPrefersOpaqueBackground) ? PlasmaCore.Dialog.SolidBackground : PlasmaCore.Dialog.StandardBackground
|
||||
|
||||
Component.onCompleted: {
|
||||
flags = flags | Qt.WindowStaysOnTopHint;
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
signal configurationChanged
|
||||
|
||||
Layout.minimumWidth: Kirigami.Units.gridUnit * 20
|
||||
Layout.minimumHeight: Math.min(Screen.height - Kirigami.Units.gridUnit * 10, implicitHeight)
|
||||
|
||||
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
property string currentPlugin: ""
|
||||
|
||||
Shortcut {
|
||||
sequence: "Escape"
|
||||
onActivated: dialog.close()
|
||||
}
|
||||
Shortcut {
|
||||
sequence: "Return"
|
||||
onActivated: root.savePluginAndClose()
|
||||
}
|
||||
Shortcut {
|
||||
sequence: "Enter"
|
||||
onActivated: root.savePluginAndClose()
|
||||
}
|
||||
|
||||
|
||||
WidgetExplorer {
|
||||
id: widgetExplorer
|
||||
provides: dialog.alternativesHelper.appletProvides
|
||||
}
|
||||
|
||||
PlasmaExtras.PlasmoidHeading {
|
||||
Kirigami.Heading {
|
||||
id: heading
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:window for widget alternatives explorer", "Alternative Widgets")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
}
|
||||
|
||||
// This timer checks with a short delay whether a new item in the list has been hovered by the cursor.
|
||||
// If not, then the cursor has left the view and thus no item should be selected.
|
||||
Timer {
|
||||
id: resetCurrentIndex
|
||||
property string oldPlugin
|
||||
interval: 100
|
||||
onTriggered: {
|
||||
if (root.currentPlugin === oldPlugin) {
|
||||
mainList.currentIndex = -1
|
||||
root.currentPlugin = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function savePluginAndClose() {
|
||||
dialog.alternativesHelper.loadAlternative(currentPlugin);
|
||||
dialog.close();
|
||||
}
|
||||
|
||||
PlasmaComponents3.ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Layout.preferredHeight: mainList.contentHeight
|
||||
|
||||
focus: true
|
||||
|
||||
ListView {
|
||||
id: mainList
|
||||
|
||||
focus: dialog.visible
|
||||
model: widgetExplorer.widgetsModel
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
highlight: PlasmaExtras.Highlight {
|
||||
pressed: mainList.currentItem && mainList.currentItem.pressed
|
||||
}
|
||||
highlightMoveDuration : 0
|
||||
highlightResizeDuration: 0
|
||||
|
||||
height: contentHeight+Kirigami.Units.smallSpacing
|
||||
|
||||
delegate: PlasmaComponents3.ItemDelegate {
|
||||
id: listItem
|
||||
|
||||
implicitHeight: contentLayout.implicitHeight + Kirigami.Units.smallSpacing * 2
|
||||
width: ListView.view.width
|
||||
|
||||
Accessible.name: model.name
|
||||
Accessible.description: model.description
|
||||
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
resetCurrentIndex.stop()
|
||||
mainList.currentIndex = index
|
||||
} else {
|
||||
resetCurrentIndex.oldPlugin = model.pluginName
|
||||
resetCurrentIndex.restart()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mainList
|
||||
function onCurrentIndexChanged() {
|
||||
if (mainList.currentIndex === index) {
|
||||
root.currentPlugin = model.pluginName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: root.savePluginAndClose()
|
||||
|
||||
Component.onCompleted: {
|
||||
if (model.pluginName === dialog.alternativesHelper.currentPlugin) {
|
||||
root.currentPlugin = model.pluginName
|
||||
setAsCurrent.restart()
|
||||
}
|
||||
}
|
||||
|
||||
// we don't want to select any entry by default
|
||||
// this cannot be set in Component.onCompleted
|
||||
Timer {
|
||||
id: setAsCurrent
|
||||
interval: 100
|
||||
onTriggered: {
|
||||
mainList.currentIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
id: contentLayout
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Kirigami.Icon {
|
||||
implicitWidth: Kirigami.Units.iconSizes.huge
|
||||
implicitHeight: Kirigami.Units.iconSizes.huge
|
||||
source: model.decoration
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: labelLayout
|
||||
|
||||
readonly property color textColor: listItem.pressed ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
spacing: 0 // The labels bring their own bottom margins
|
||||
|
||||
Kirigami.Heading {
|
||||
level: 4
|
||||
Layout.fillWidth: true
|
||||
text: model.name
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
type: model.pluginName === dialog.alternativesHelper.currentPlugin ? PlasmaExtras.Heading.Type.Primary : PlasmaExtras.Heading.Type.Normal
|
||||
color: labelLayout.textColor
|
||||
}
|
||||
|
||||
PlasmaComponents3.Label {
|
||||
Layout.fillWidth: true
|
||||
text: model.description
|
||||
textFormat: Text.PlainText
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
font.family: Kirigami.Theme.smallFont.family
|
||||
font.bold: model.pluginName === dialog.alternativesHelper.currentPlugin
|
||||
opacity: 0.75
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
color: labelLayout.textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
|
||||
SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.components as PlasmaComponents
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.kwindowsystem
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.graphicaleffects as KGraphicalEffects
|
||||
|
||||
Item {
|
||||
id: delegate
|
||||
|
||||
readonly property string pluginName: model.pluginName
|
||||
readonly property bool pendingUninstall: pendingUninstallTimer.applets.indexOf(pluginName) > -1
|
||||
readonly property bool pressed: tapHandler.pressed
|
||||
readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software
|
||||
|
||||
width: list.cellWidth
|
||||
height: list.cellHeight
|
||||
|
||||
Accessible.name: i18nc("@action:button accessible only, %1 is widget name", "Add %1", model.name) + (model.isSupported ? "" : unsupportedTooltip.mainText)
|
||||
Accessible.description: (model.isSupported ? "" : model.unsupportedMessage) + model.description + (overlayedBadge.visible ? countLabel.Accessible.name : "")
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
onHoveredChanged: if (hovered) delegate.GridView.view.currentIndex = index
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
id: tapHandler
|
||||
enabled: !delegate.pendingUninstall && model.isSupported
|
||||
onTapped: widgetExplorer.addApplet(delegate.pluginName)
|
||||
}
|
||||
|
||||
PlasmaCore.ToolTipArea {
|
||||
id: unsupportedTooltip
|
||||
anchors.fill: parent
|
||||
visible: !model.isSupported
|
||||
mainText: i18nc("@info:tooltip", "Unsupported Widget")
|
||||
subText: model.unsupportedMessage
|
||||
}
|
||||
|
||||
// Avoid repositioning delegate item after dragFinished
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
enabled: model.isSupported
|
||||
|
||||
Drag.dragType: Drag.Automatic
|
||||
Drag.supportedActions: Qt.MoveAction | Qt.LinkAction
|
||||
Drag.mimeData: {
|
||||
"text/x-plasmoidservicename" : delegate.pluginName,
|
||||
}
|
||||
Drag.onDragStarted: {
|
||||
KWindowSystem.showingDesktop = true;
|
||||
main.draggingWidget = true;
|
||||
delegate.forceActiveFocus()
|
||||
}
|
||||
Drag.onDragFinished: {
|
||||
main.draggingWidget = false;
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
id: dragHandler
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
enabled: !delegate.pendingUninstall && model.isSupported
|
||||
|
||||
onActiveChanged: if (active) {
|
||||
iconContainer.grabToImage(function(result) {
|
||||
if (!dragHandler.active) {
|
||||
return;
|
||||
}
|
||||
parent.Drag.imageSource = result.url;
|
||||
parent.Drag.active = dragHandler.active;
|
||||
}, Qt.size(Kirigami.Units.iconSizes.huge, Kirigami.Units.iconSizes.huge));
|
||||
} else {
|
||||
parent.Drag.active = false;
|
||||
parent.Drag.imageSource = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DragHandler {
|
||||
id: touchDragHandler
|
||||
acceptedDevices: PointerDevice.Stylus | PointerDevice.TouchScreen
|
||||
enabled: dragHandler.enabled
|
||||
yAxis.enabled: false
|
||||
|
||||
onActiveChanged: if (active) {
|
||||
iconContainer.grabToImage(function(result) {
|
||||
if (!touchDragHandler.active) {
|
||||
return;
|
||||
}
|
||||
parent.Drag.imageSource = result.url;
|
||||
parent.Drag.active = touchDragHandler.active;
|
||||
}, Qt.size(Kirigami.Units.iconSizes.huge, Kirigami.Units.iconSizes.huge));
|
||||
} else {
|
||||
parent.Drag.active = false;
|
||||
parent.Drag.imageSource = "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
|
||||
readonly property color textColor: tapHandler.pressed ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
//bottom: parent.bottom
|
||||
margins: Kirigami.Units.smallSpacing * 2
|
||||
rightMargin: Kirigami.Units.smallSpacing * 2 // don't cram the text to the border too much
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
Item {
|
||||
id: iconContainer
|
||||
width: Kirigami.Units.iconSizes.enormous
|
||||
height: width
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
opacity: delegate.pendingUninstall ? 0.6 : 1
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: iconWidget
|
||||
anchors.fill: parent
|
||||
Kirigami.Icon {
|
||||
anchors.fill: parent
|
||||
source: model.decoration
|
||||
visible: model.screenshot === ""
|
||||
selected: tapHandler.pressed
|
||||
enabled: model.isSupported
|
||||
}
|
||||
Image {
|
||||
width: Kirigami.Units.iconSizes.enormous
|
||||
height: width
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: model.screenshot
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: badgeMask
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
x: Math.round(-Kirigami.Units.smallSpacing * 1.5 / 2)
|
||||
y: x
|
||||
width: overlayedBadge.width + Math.round(Kirigami.Units.smallSpacing * 1.5)
|
||||
height: overlayedBadge.height + Math.round(Kirigami.Units.smallSpacing * 1.5)
|
||||
radius: height
|
||||
visible: (running && delegate.GridView.isCurrentItem) ?? false
|
||||
}
|
||||
}
|
||||
|
||||
KGraphicalEffects.BadgeEffect {
|
||||
anchors.fill: parent
|
||||
source: ShaderEffectSource {
|
||||
sourceItem: iconWidget
|
||||
hideSource: !softwareRendering
|
||||
live: false
|
||||
}
|
||||
mask: ShaderEffectSource {
|
||||
id: maskShaderSource
|
||||
sourceItem: badgeMask
|
||||
hideSource: true
|
||||
live: false
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: overlayedBadge
|
||||
width: countLabel.width + height
|
||||
height: Math.round(Kirigami.Units.iconSizes.sizeForLabels * 1.3)
|
||||
radius: height
|
||||
color: (running && delegate.GridView.isCurrentItem) ? Kirigami.Theme.highlightColor : Kirigami.Theme.positiveTextColor
|
||||
visible: ((running && delegate.GridView.isCurrentItem) || model.recent) ?? false
|
||||
onVisibleChanged: maskShaderSource.scheduleUpdate()
|
||||
|
||||
PlasmaComponents.Label {
|
||||
id: countLabel
|
||||
height: parent.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.centerIn: parent
|
||||
text: (running && delegate.GridView.isCurrentItem) ? running : i18ndc("plasma_shell_org.kde.plasma.desktop", "Text displayed on top of newly installed widgets", "New!")
|
||||
Accessible.name: running
|
||||
? i18ncp("@info:other accessible for badge showing applet count", "%1 widget active", "%1 widgets active", running)
|
||||
: i18nc(" @info:other accessible for badge indicating new widget", "Recently installed")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
id: uninstallButton
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
}
|
||||
text: delegate.pendingUninstall ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button tooltip and accessible cancel uninstall widget", "Cancel Uninstallation")
|
||||
: i18ndc("plasma_shell_org.kde.plasma.desktop","@action:button tooltip and accessible uninstall widget", "Mark for Uninstallation")
|
||||
icon.name: delegate.pendingUninstall ? "edit-undo" : "edit-delete"
|
||||
display: PlasmaComponents.AbstractButton.IconOnly
|
||||
Accessible.description: delegate.pendingUninstall
|
||||
? i18nc("@action:button accessible only, %1 is widget name", "Cancel pending uninstallation for widget %1", model.name)
|
||||
: i18nc("@action:button accessible only, %1 is widget name", "Mark widget %1 for uninstallation. Requires confirmation", model.name)
|
||||
// we don't really "undo" anything but we'll pretend to the user that we do
|
||||
PlasmaComponents.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
PlasmaComponents.ToolTip.visible: hovered
|
||||
PlasmaComponents.ToolTip.text: text
|
||||
flat: false
|
||||
visible: (model.local && delegate.GridView.isCurrentItem && !dragHandler.active && !touchDragHandler.active) ?? false
|
||||
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
// hovering the uninstall button triggers onExited of the main mousearea
|
||||
delegate.GridView.view.currentIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
let pending = pendingUninstallTimer.applets
|
||||
if (delegate.pendingUninstall) {
|
||||
let index = pending.indexOf(pluginName)
|
||||
if (index > -1) {
|
||||
pending.splice(index, 1)
|
||||
}
|
||||
} else {
|
||||
pending.push(pluginName)
|
||||
}
|
||||
pendingUninstallTimer.applets = pending
|
||||
|
||||
if (pending.length) {
|
||||
pendingUninstallTimer.restart()
|
||||
} else {
|
||||
pendingUninstallTimer.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
id: removeInstancesButton
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: uninstallButton.visible ? uninstallButton.left : parent.right
|
||||
rightMargin: uninstallButton.visible ? Kirigami.Units.smallSpacing : 0
|
||||
}
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Remove all instances")
|
||||
display: PlasmaComponents.AbstractButton.IconOnly
|
||||
icon.name: "edit-clear-all"
|
||||
Accessible.description: i18ncp("@action:button accessible description, %1 number of instances %2 is widget name",
|
||||
"Remove running instance of widget %2",
|
||||
"Remove all %1 running instances of widget %2", running ,model.name)
|
||||
|
||||
// we don't really "undo" anything but we'll pretend to the user that we do
|
||||
PlasmaComponents.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
PlasmaComponents.ToolTip.visible: hovered
|
||||
PlasmaComponents.ToolTip.text: text
|
||||
flat: false
|
||||
visible: (running && delegate.GridView.isCurrentItem && !dragHandler.active) ?? false
|
||||
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
// hovering the uninstall button triggers onExited of the main mousearea
|
||||
delegate.GridView.view.currentIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: widgetExplorer.removeAllInstances(pluginName)
|
||||
}
|
||||
}
|
||||
Kirigami.Heading {
|
||||
id: heading
|
||||
Layout.fillWidth: true
|
||||
level: 4
|
||||
text: model.name
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 3
|
||||
lineHeight: 0.95
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: mainLayout.textColor
|
||||
}
|
||||
PlasmaComponents.Label {
|
||||
Layout.fillWidth: true
|
||||
// otherwise causes binding loop due to the way the Plasma sets the height
|
||||
text: model.description
|
||||
textFormat: Text.PlainText
|
||||
font: Kirigami.Theme.smallFont
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 5 - heading.lineCount
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: mainLayout.textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
|
||||
import org.kde.plasma.components as PC3
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.kquickcontrolsaddons
|
||||
import org.kde.kwindowsystem
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.private.shell
|
||||
|
||||
PC3.Page {
|
||||
id: main
|
||||
|
||||
width: Math.max(heading.paintedWidth, Kirigami.Units.iconSizes.enormous * 3 + Kirigami.Units.smallSpacing * 4 + Kirigami.Units.gridUnit * 5)
|
||||
height: 800//Screen.height
|
||||
|
||||
opacity: draggingWidget ? 0.3 : 1
|
||||
|
||||
readonly property int contentMargins: Kirigami.Units.largeSpacing
|
||||
|
||||
property QtObject containment
|
||||
|
||||
property PlasmaCore.Dialog sidePanel
|
||||
|
||||
//external drop events can cause a raise event causing us to lose focus and
|
||||
//therefore get deleted whilst we are still in a drag exec()
|
||||
//this is a clue to the owning dialog that hideOnWindowDeactivate should be deleted
|
||||
//See https://bugs.kde.org/show_bug.cgi?id=332733
|
||||
property bool preventWindowHide: draggingWidget || categoriesDialog.status !== PlasmaExtras.Menu.Closed
|
||||
|| getWidgetsDialog.status !== PlasmaExtras.Menu.Closed
|
||||
|
||||
// We might've lost focus during the widget drag and drop or whilst using
|
||||
// the "get widgets" dialog; however we prevented the sidebar to hide.
|
||||
// This might get the sidebar stuck, since we only hide when losing focus.
|
||||
// To avoid this we reclaim focus as soon as the drag and drop is done,
|
||||
// or the get widgets window is closed.
|
||||
onPreventWindowHideChanged: {
|
||||
if (!preventWindowHide && !sidePanel.active) {
|
||||
sidePanel.requestActivate()
|
||||
}
|
||||
}
|
||||
|
||||
property bool outputOnly: draggingWidget
|
||||
|
||||
property Item categoryButton
|
||||
|
||||
property bool draggingWidget: false
|
||||
|
||||
signal closed()
|
||||
|
||||
onClosed: {
|
||||
// If was called from a panel, open the panel config
|
||||
if (root.widgetExplorer.containment &&
|
||||
root.widgetExplorer.containment.containmentType == 1 &&
|
||||
!root.widgetExplorer.containment.userConfiguring) {
|
||||
root.widgetExplorer.containment.internalAction("configure").trigger()
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
KWindowSystem.showingDesktop = false
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!root.widgetExplorer) {
|
||||
root.widgetExplorer = widgetExplorerComponent.createObject(root)
|
||||
}
|
||||
root.widgetExplorer.containment = main.containment
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
if (pendingUninstallTimer.running) {
|
||||
// we're not being destroyed so at least reset the filters
|
||||
widgetExplorer.widgetsModel.filterQuery = ""
|
||||
widgetExplorer.widgetsModel.filterType = ""
|
||||
widgetExplorer.widgetsModel.searchTerm = ""
|
||||
} else {
|
||||
root.widgetExplorer.destroy()
|
||||
root.widgetExplorer = null
|
||||
}
|
||||
}
|
||||
|
||||
function addCurrentApplet() {
|
||||
var pluginName = list.currentItem ? list.currentItem.pluginName : ""
|
||||
if (pluginName) {
|
||||
widgetExplorer.addApplet(pluginName)
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
shortcut: "Escape"
|
||||
onTriggered: {
|
||||
if (searchInput.length > 0) {
|
||||
searchInput.text = ""
|
||||
} else {
|
||||
main.closed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
shortcut: "Enter"
|
||||
onTriggered: addCurrentApplet()
|
||||
}
|
||||
QQC2.Action {
|
||||
shortcut: "Return"
|
||||
onTriggered: addCurrentApplet()
|
||||
}
|
||||
|
||||
Component {
|
||||
id: widgetExplorerComponent
|
||||
|
||||
WidgetExplorer {
|
||||
//view: desktop
|
||||
onShouldClose: main.closed();
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaExtras.ModelContextMenu {
|
||||
id: categoriesDialog
|
||||
visualParent: categoryButton
|
||||
// model set on first invocation
|
||||
|
||||
onClicked: model => {
|
||||
list.contentX = 0
|
||||
list.contentY = 0
|
||||
categoryButton.text = (model.filterData ? model.display : i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button like listbox, switches category to all widgets", "All Widgets"))
|
||||
widgetExplorer.widgetsModel.filterQuery = model.filterData
|
||||
widgetExplorer.widgetsModel.filterType = model.filterType
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaExtras.ModelContextMenu {
|
||||
id: getWidgetsDialog
|
||||
visualParent: getWidgetsButton
|
||||
placement: PlasmaExtras.Menu.TopPosedLeftAlignedPopup
|
||||
// model set on first invocation
|
||||
onClicked: model.trigger()
|
||||
}
|
||||
|
||||
header: PlasmaExtras.PlasmoidHeading {
|
||||
// Subtract page's own margins since we touch the top, left, and right
|
||||
topPadding: - main.sidePanel.margins.top
|
||||
leftPadding: main.contentMargins - main.sidePanel.margins.left
|
||||
rightPadding: main.contentMargins - main.sidePanel.margins.right
|
||||
bottomPadding: main.contentMargins
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
Kirigami.Heading {
|
||||
id: heading
|
||||
level: 1
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@title:group for widget grid", "Widgets")
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
PC3.ToolButton {
|
||||
id: getWidgetsButton
|
||||
icon.name: "get-hot-new-stuff"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button The word 'new' refers to widgets", "Get New…")
|
||||
Accessible.name: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Get New Widgets…")
|
||||
|
||||
|
||||
KeyNavigation.right: closeButton
|
||||
KeyNavigation.down: searchInput
|
||||
|
||||
onClicked: {
|
||||
getWidgetsDialog.model = widgetExplorer.widgetsMenuActions
|
||||
getWidgetsDialog.openRelative()
|
||||
}
|
||||
}
|
||||
PC3.ToolButton {
|
||||
id: closeButton
|
||||
text: i18nc("@action:button accessible for close button", "Close Widget Explorer")
|
||||
icon.name: "window-close"
|
||||
display: PC3.AbstractButton.IconOnly
|
||||
|
||||
KeyNavigation.down: categoryButton
|
||||
|
||||
onClicked: main.closed()
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
PlasmaExtras.SearchField {
|
||||
id: searchInput
|
||||
Layout.fillWidth: true
|
||||
Accessible.name: i18nc("@label:textbox accessible", "Search through widgets")
|
||||
|
||||
KeyNavigation.down: list
|
||||
KeyNavigation.right: categoryButton
|
||||
|
||||
onTextChanged: {
|
||||
list.positionViewAtBeginning()
|
||||
list.currentIndex = -1
|
||||
widgetExplorer.widgetsModel.searchTerm = text
|
||||
}
|
||||
|
||||
Component.onCompleted: if (!Kirigami.InputMethod.willShowOnActive) { forceActiveFocus() }
|
||||
}
|
||||
PC3.ToolButton {
|
||||
id: categoryButton
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button like listbox, switches category to all widgets", "All Widgets")
|
||||
icon.name: "view-filter"
|
||||
|
||||
Accessible.role: Accessible.ButtonMenu
|
||||
down: categoriesDialog.status == PlasmaExtras.ModelContextMenu.Open || pressed
|
||||
|
||||
KeyNavigation.down: list
|
||||
|
||||
onClicked: {
|
||||
categoriesDialog.model = widgetExplorer.filterModel
|
||||
categoriesDialog.open(0, categoryButton.height)
|
||||
}
|
||||
|
||||
PC3.ToolTip {
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button tooltip only", "Categories")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: PlasmaExtras.PlasmoidHeading {
|
||||
id: footerContainer
|
||||
|
||||
// Subtract page's own margins since we touch the left, right, and bottom
|
||||
topPadding: main.contentMargins
|
||||
leftPadding: main.contentMargins - main.sidePanel.margins.left
|
||||
rightPadding: main.contentMargins - main.sidePanel.margins.right
|
||||
bottomPadding: main.contentMargins - main.sidePanel.margins.bottom
|
||||
|
||||
position: PC3.ToolBar.Footer
|
||||
visible: pendingUninstallTimer.applets.length > 0
|
||||
|
||||
contentItem: PC3.Button {
|
||||
id: uninstallButton
|
||||
icon.name: "delete"
|
||||
text: i18ndcp("plasma_shell_org.kde.plasma.desktop", "@action:button uninstall widgets in widget explorer", "Uninstall (%1 Widget)", "Uninstall (%1 Widgets)", pendingUninstallTimer.applets.length)
|
||||
onClicked: pendingUninstallTimer.uninstall()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: setModelTimer
|
||||
interval: 20
|
||||
running: true
|
||||
onTriggered: list.model = widgetExplorer.widgetsModel
|
||||
}
|
||||
|
||||
PC3.ScrollView {
|
||||
id: scrollView
|
||||
anchors {
|
||||
fill: parent
|
||||
// Subtract page's own margins since we touch the left, right, and sometimes bottom
|
||||
topMargin: -1 // account for PlasmoidHeading's pixel
|
||||
leftMargin: - main.sidePanel.margins.left
|
||||
rightMargin: - main.sidePanel.margins.right
|
||||
bottomMargin: footerContainer.visible ? 0 : - main.sidePanel.margins.bottom
|
||||
}
|
||||
|
||||
// The scrollbar changing visibility can lead to the content size changing due to word wrap
|
||||
// A delayed binding gives some extra time, it'll come to a stop as there's only one scroll bar
|
||||
property bool scrollBarVisible
|
||||
Binding on scrollBarVisible {
|
||||
value: list.contentHeight > scrollView.height
|
||||
delayed: true
|
||||
}
|
||||
|
||||
PC3.ScrollBar.horizontal.policy: PC3.ScrollBar.AlwaysOff
|
||||
PC3.ScrollBar.vertical.visible: scrollBarVisible
|
||||
|
||||
// hide the flickering by fading in nicely
|
||||
opacity: setModelTimer.running ? 0 : 1
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: list
|
||||
|
||||
readonly property int effectiveWidth: width
|
||||
- leftMargin
|
||||
- rightMargin
|
||||
|
||||
// model set delayed by Timer above
|
||||
|
||||
topMargin: main.contentMargins
|
||||
leftMargin: main.contentMargins
|
||||
rightMargin: main.contentMargins
|
||||
bottomMargin: main.contentMargins
|
||||
|
||||
activeFocusOnTab: true
|
||||
cellWidth: Math.floor(effectiveWidth / 3)
|
||||
cellHeight: Kirigami.Units.iconSizes.enormous + Kirigami.Units.smallSpacing * 4 + headingFontMetrics.height * 3 * referenceHeading.lineHeight + descriptionFontMetrics.height * 2
|
||||
|
||||
// This element is used as a reference to size the
|
||||
// cellHeight and should be kept in sync with the
|
||||
// heading within the AppletDelegate.
|
||||
Kirigami.Heading {
|
||||
id: referenceHeading
|
||||
visible: false
|
||||
level: 4
|
||||
lineHeight: 0.95
|
||||
}
|
||||
FontMetrics {
|
||||
id: headingFontMetrics
|
||||
font: referenceHeading.font
|
||||
}
|
||||
FontMetrics {
|
||||
id: descriptionFontMetrics
|
||||
font: Kirigami.Theme.smallFont
|
||||
}
|
||||
|
||||
delegate: AppletDelegate {}
|
||||
highlight: PlasmaExtras.Highlight {
|
||||
pressed: list.currentItem && list.currentItem.pressed
|
||||
}
|
||||
highlightMoveDuration: 0
|
||||
//highlightResizeDuration: 0
|
||||
|
||||
//slide in to view from the left
|
||||
add: Transition {
|
||||
// Work around https://bugreports.qt.io/browse/QTBUG-127709
|
||||
enabled: Kirigami.Units.shortDuration > 0
|
||||
NumberAnimation {
|
||||
properties: "x"
|
||||
from: -list.width
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
//slide out of view to the right
|
||||
remove: Transition {
|
||||
// Work around https://bugreports.qt.io/browse/QTBUG-127709
|
||||
enabled: Kirigami.Units.shortDuration > 0
|
||||
NumberAnimation {
|
||||
properties: "x"
|
||||
to: list.width
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
//if we are adding other items into the view use the same animation as normal adding
|
||||
//this makes everything slide in together
|
||||
//if we make it move everything ends up weird
|
||||
addDisplaced: list.add
|
||||
|
||||
//moved due to filtering
|
||||
displaced: Transition {
|
||||
NumberAnimation {
|
||||
properties: "x,y"
|
||||
duration: Kirigami.Units.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.up: searchInput
|
||||
KeyNavigation.down: uninstallButton
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaExtras.PlaceholderMessage {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - (Kirigami.Units.gridUnit * 4)
|
||||
iconName: "edit-none"
|
||||
text: searchInput.text.length > 0 ? i18ndc("plasma_shell_org.kde.plasma.desktop", "@info placeholdermessage", "No widgets matched the search terms") : i18ndc("plasma_shell_org.kde.plasma.desktop", "@info placeholdermessage", "No widgets available")
|
||||
visible: list.count == 0 && !setModelTimer.running
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import org.kde.ksvg as KSvg
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.workspace.osd
|
||||
|
||||
KSvg.FrameSvgItem {
|
||||
id: osd
|
||||
|
||||
property alias timeout: osdItem.timeout
|
||||
property alias osdValue: osdItem.osdValue
|
||||
property alias osdMaxValue: osdItem.osdMaxValue
|
||||
property alias icon: osdItem.icon
|
||||
property alias showingProgress: osdItem.showingProgress
|
||||
|
||||
objectName: "onScreenDisplay"
|
||||
visible: false
|
||||
width: osdItem.width + margins.left + margins.right
|
||||
height: osdItem.height + margins.top + margins.bottom
|
||||
imagePath: "dialogs/background"
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
|
||||
function show() {
|
||||
osd.visible = true;
|
||||
hideAnimation.restart();
|
||||
}
|
||||
|
||||
// avoid leaking ColorScope of lock screen theme into the OSD "popup"
|
||||
Item {
|
||||
width: osdItem.width
|
||||
height: osdItem.height
|
||||
anchors.centerIn: parent
|
||||
|
||||
OsdItem {
|
||||
id: osdItem
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: hideAnimation
|
||||
ScriptAction {
|
||||
// prevent opacity layering of ProgressBar.
|
||||
script: osd.layer.enabled = true
|
||||
}
|
||||
// prevent press and hold from flickering
|
||||
PauseAnimation { duration: osd.timeout }
|
||||
NumberAnimation {
|
||||
target: osd
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: Kirigami.Units.shortDuration
|
||||
easing.type: Easing.InQuad
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
osd.visible = false;
|
||||
osd.opacity = 1;
|
||||
osd.icon = "";
|
||||
osd.osdValue = 0;
|
||||
osd.layer.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool debug: false
|
||||
property string notification
|
||||
signal clearPassword()
|
||||
signal notificationRepeated()
|
||||
|
||||
// These are magical properties that kscreenlocker looks for
|
||||
property bool viewVisible: false
|
||||
property bool suspendToRamSupported: false
|
||||
property bool suspendToDiskSupported: false
|
||||
|
||||
// These are magical signals that kscreenlocker looks for
|
||||
signal suspendToDisk()
|
||||
signal suspendToRam()
|
||||
|
||||
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
implicitWidth: 800
|
||||
implicitHeight: 600
|
||||
|
||||
LockScreenUi {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQml
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import org.kde.plasma.components as PlasmaComponents3
|
||||
import org.kde.plasma.workspace.components as PW
|
||||
import org.kde.plasma.private.keyboardindicator as KeyboardIndicator
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kscreenlocker as ScreenLocker
|
||||
|
||||
import org.kde.plasma.private.sessions
|
||||
import org.kde.breeze.components
|
||||
|
||||
Item {
|
||||
id: lockScreenUi
|
||||
|
||||
// If we're using software rendering, draw outlines instead of shadows
|
||||
// See https://bugs.kde.org/show_bug.cgi?id=398317
|
||||
readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software
|
||||
|
||||
function handleMessage(msg) {
|
||||
if (!root.notification) {
|
||||
root.notification += msg;
|
||||
} else if (root.notification.includes(msg)) {
|
||||
root.notificationRepeated();
|
||||
} else {
|
||||
root.notification += "\n" + msg
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
|
||||
Connections {
|
||||
target: authenticator
|
||||
function onFailed(kind) {
|
||||
if (kind != 0) { // if this is coming from the noninteractive authenticators
|
||||
return;
|
||||
}
|
||||
const msg = i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:status", "Unlocking failed");
|
||||
lockScreenUi.handleMessage(msg);
|
||||
graceLockTimer.restart();
|
||||
notificationRemoveTimer.restart();
|
||||
rejectPasswordAnimation.start();
|
||||
}
|
||||
|
||||
function onSucceeded() {
|
||||
if (authenticator.hadPrompt) {
|
||||
Qt.quit();
|
||||
} else {
|
||||
mainStack.replace(null, Qt.resolvedUrl("NoPasswordUnlock.qml"),
|
||||
{
|
||||
userListModel: users
|
||||
},
|
||||
StackView.Immediate,
|
||||
);
|
||||
mainStack.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
function onInfoMessageChanged() {
|
||||
lockScreenUi.handleMessage(authenticator.infoMessage);
|
||||
}
|
||||
|
||||
function onErrorMessageChanged() {
|
||||
lockScreenUi.handleMessage(authenticator.errorMessage);
|
||||
}
|
||||
|
||||
function onPromptChanged(msg) {
|
||||
lockScreenUi.handleMessage(authenticator.prompt);
|
||||
}
|
||||
function onPromptForSecretChanged(msg) {
|
||||
mainBlock.showPassword = false;
|
||||
mainBlock.mainPasswordBox.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
SessionManagement {
|
||||
id: sessionManagement
|
||||
}
|
||||
|
||||
KeyboardIndicator.KeyState {
|
||||
id: capsLockState
|
||||
key: Qt.Key_CapsLock
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: sessionManagement
|
||||
function onAboutToSuspend() {
|
||||
root.clearPassword();
|
||||
}
|
||||
}
|
||||
|
||||
RejectPasswordAnimation {
|
||||
id: rejectPasswordAnimation
|
||||
target: mainBlock
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: lockScreenRoot
|
||||
|
||||
property bool uiVisible: false
|
||||
property bool seenPositionChange: false
|
||||
property bool blockUI: containsMouse && (mainStack.depth > 1 || mainBlock.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive)
|
||||
|
||||
x: parent.x
|
||||
y: parent.y
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
hoverEnabled: true
|
||||
cursorShape: uiVisible ? Qt.ArrowCursor : Qt.BlankCursor
|
||||
drag.filterChildren: true
|
||||
onPressed: uiVisible = true;
|
||||
onPositionChanged: {
|
||||
uiVisible = seenPositionChange;
|
||||
seenPositionChange = true;
|
||||
}
|
||||
onUiVisibleChanged: {
|
||||
if (uiVisible) {
|
||||
Window.window.requestActivate();
|
||||
}
|
||||
|
||||
if (blockUI) {
|
||||
fadeoutTimer.running = false;
|
||||
} else if (uiVisible) {
|
||||
fadeoutTimer.restart();
|
||||
}
|
||||
authenticator.startAuthenticating();
|
||||
}
|
||||
onBlockUIChanged: {
|
||||
if (blockUI) {
|
||||
fadeoutTimer.running = false;
|
||||
uiVisible = true;
|
||||
} else {
|
||||
fadeoutTimer.restart();
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
uiVisible = false;
|
||||
}
|
||||
Keys.onEscapePressed: {
|
||||
// If the escape key is pressed, kscreenlocker will turn off the screen.
|
||||
// We do not want to show the password prompt in this case.
|
||||
if (uiVisible) {
|
||||
uiVisible = false;
|
||||
if (inputPanel.keyboardActive) {
|
||||
inputPanel.showHide();
|
||||
}
|
||||
root.clearPassword();
|
||||
}
|
||||
}
|
||||
Keys.onPressed: event => {
|
||||
uiVisible = true;
|
||||
event.accepted = false;
|
||||
}
|
||||
Timer {
|
||||
id: fadeoutTimer
|
||||
interval: 10000
|
||||
onTriggered: {
|
||||
if (!lockScreenRoot.blockUI) {
|
||||
mainBlock.mainPasswordBox.showPassword = false;
|
||||
lockScreenRoot.uiVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: notificationRemoveTimer
|
||||
interval: 3000
|
||||
onTriggered: root.notification = ""
|
||||
}
|
||||
Timer {
|
||||
id: graceLockTimer
|
||||
interval: 3000
|
||||
onTriggered: {
|
||||
root.clearPassword();
|
||||
authenticator.startAuthenticating();
|
||||
}
|
||||
}
|
||||
|
||||
PropertyAnimation {
|
||||
id: launchAnimation
|
||||
target: lockScreenRoot
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Kirigami.Units.veryLongDuration * 2
|
||||
}
|
||||
|
||||
Component.onCompleted: launchAnimation.start();
|
||||
|
||||
WallpaperFader {
|
||||
anchors.fill: parent
|
||||
state: lockScreenRoot.uiVisible ? "on" : "off"
|
||||
source: wallpaper
|
||||
mainStack: mainStack
|
||||
footer: footer
|
||||
clock: clock
|
||||
alwaysShowClock: config.alwaysShowClock && !config.hideClockWhenIdle
|
||||
}
|
||||
|
||||
DropShadow {
|
||||
id: clockShadow
|
||||
anchors.fill: clock
|
||||
source: clock
|
||||
visible: !lockScreenUi.softwareRendering && config.alwaysShowClock
|
||||
radius: 7
|
||||
verticalOffset: 0.8
|
||||
samples: 15
|
||||
spread: 0.2
|
||||
color : Qt.rgba(0, 0, 0, 0.7)
|
||||
opacity: lockScreenRoot.uiVisible ? 0 : 1
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: Kirigami.Units.veryLongDuration * 2
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Clock {
|
||||
id: clock
|
||||
property Item shadow: clockShadow
|
||||
visible: y > 0 && config.alwaysShowClock
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: (mainBlock.userList.y + mainStack.y)/2 - height/2
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: users
|
||||
|
||||
Component.onCompleted: {
|
||||
users.append({
|
||||
name: kscreenlocker_userName,
|
||||
realName: kscreenlocker_userName,
|
||||
icon: kscreenlocker_userImage !== ""
|
||||
? "file://" + kscreenlocker_userImage.split("/").map(encodeURIComponent).join("/")
|
||||
: "",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: mainStack
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
height: lockScreenRoot.height + Kirigami.Units.gridUnit * 3
|
||||
focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it
|
||||
|
||||
// this isn't implicit, otherwise items still get processed for the scenegraph
|
||||
visible: opacity > 0
|
||||
|
||||
initialItem: MainBlock {
|
||||
id: mainBlock
|
||||
lockScreenUiVisible: lockScreenRoot.uiVisible
|
||||
|
||||
showUserList: userList.y + mainStack.y > 0
|
||||
|
||||
enabled: !graceLockTimer.running
|
||||
|
||||
StackView.onStatusChanged: {
|
||||
// prepare for presenting again to the user
|
||||
if (StackView.status === StackView.Activating) {
|
||||
mainPasswordBox.clear();
|
||||
mainPasswordBox.focus = true;
|
||||
root.notification = "";
|
||||
}
|
||||
}
|
||||
userListModel: users
|
||||
|
||||
|
||||
notificationMessage: {
|
||||
const parts = [];
|
||||
if (capsLockState.locked) {
|
||||
parts.push(i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:status", "Caps Lock is on"));
|
||||
}
|
||||
if (root.notification) {
|
||||
parts.push(root.notification);
|
||||
}
|
||||
return parts.join(" • ");
|
||||
}
|
||||
|
||||
onPasswordResult: password => {
|
||||
authenticator.respond(password)
|
||||
}
|
||||
|
||||
actionItems: [
|
||||
ActionButton {
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Slee&p")
|
||||
icon.name: "system-suspend"
|
||||
onClicked: root.suspendToRam()
|
||||
visible: root.suspendToRamSupported
|
||||
},
|
||||
ActionButton {
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "&Hibernate")
|
||||
icon.name: "system-suspend-hibernate"
|
||||
onClicked: root.suspendToDisk()
|
||||
visible: root.suspendToDiskSupported
|
||||
},
|
||||
ActionButton {
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Switch &User")
|
||||
icon.name: "system-switch-user"
|
||||
onClicked: {
|
||||
sessionManagement.switchUser();
|
||||
}
|
||||
visible: sessionManagement.canSwitchUser
|
||||
}
|
||||
]
|
||||
|
||||
Loader {
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing // some distance to the password field
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: item ? item.implicitHeight : 0
|
||||
active: config.showMediaControls
|
||||
source: "MediaControls.qml"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VirtualKeyboardLoader {
|
||||
id: inputPanel
|
||||
|
||||
z: 1
|
||||
|
||||
screenRoot: lockScreenRoot
|
||||
mainStack: mainStack
|
||||
mainBlock: mainBlock
|
||||
passwordField: mainBlock.mainPasswordBox
|
||||
}
|
||||
|
||||
Loader {
|
||||
z: 2
|
||||
active: root.viewVisible
|
||||
source: "LockOsd.qml"
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: Kirigami.Units.gridUnit
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Containment masks stretch clickable area of their buttons to
|
||||
// the screen edges, essentially making them adhere to Fitts's law.
|
||||
// Due to virtual keyboard button having an icon, buttons may have
|
||||
// different heights, so fillHeight is required.
|
||||
//
|
||||
// Note for contributors: Keep this in sync with SDDM Main.qml footer.
|
||||
RowLayout {
|
||||
id: footer
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: Kirigami.Units.smallSpacing
|
||||
}
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
PlasmaComponents3.ToolButton {
|
||||
id: virtualKeyboardButton
|
||||
|
||||
focusPolicy: Qt.TabFocus
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "Button to show/hide virtual keyboard", "Virtual Keyboard")
|
||||
icon.name: inputPanel.keyboardActive ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off"
|
||||
onClicked: {
|
||||
// Otherwise the password field loses focus and virtual keyboard
|
||||
// keystrokes get eaten
|
||||
mainBlock.mainPasswordBox.forceActiveFocus();
|
||||
inputPanel.showHide()
|
||||
}
|
||||
|
||||
visible: inputPanel.status === Loader.Ready
|
||||
|
||||
Layout.fillHeight: true
|
||||
containmentMask: Item {
|
||||
parent: virtualKeyboardButton
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: -footer.anchors.margins
|
||||
anchors.bottomMargin: -footer.anchors.margins
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaComponents3.ToolButton {
|
||||
id: keyboardButton
|
||||
|
||||
focusPolicy: Qt.TabFocus
|
||||
Accessible.description: i18ndc("plasma_shell_org.kde.plasma.desktop", "Button to change keyboard layout", "Switch layout")
|
||||
icon.name: "input-keyboard"
|
||||
|
||||
PW.KeyboardLayoutSwitcher {
|
||||
id: keyboardLayoutSwitcher
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
|
||||
text: keyboardLayoutSwitcher.layoutNames.longName
|
||||
onClicked: keyboardLayoutSwitcher.keyboardLayout.switchToNextLayout()
|
||||
|
||||
visible: keyboardLayoutSwitcher.hasMultipleKeyboardLayouts
|
||||
|
||||
Layout.fillHeight: true
|
||||
containmentMask: Item {
|
||||
parent: keyboardButton
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: virtualKeyboardButton.visible ? 0 : -footer.anchors.margins
|
||||
anchors.bottomMargin: -footer.anchors.margins
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Battery {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2016 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls as QQC2
|
||||
|
||||
import org.kde.plasma.components as PlasmaComponents3
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kscreenlocker as ScreenLocker
|
||||
|
||||
import org.kde.breeze.components
|
||||
|
||||
SessionManagementScreen {
|
||||
id: sessionManager
|
||||
|
||||
readonly property alias mainPasswordBox: passwordBox
|
||||
property bool lockScreenUiVisible: false
|
||||
property alias showPassword: passwordBox.showPassword
|
||||
|
||||
//the y position that should be ensured visible when the on screen keyboard is visible
|
||||
property int visibleBoundary: mapFromItem(loginButton, 0, 0).y
|
||||
onHeightChanged: visibleBoundary = mapFromItem(loginButton, 0, 0).y + loginButton.height + Kirigami.Units.smallSpacing
|
||||
/*
|
||||
* Login has been requested with the following username and password
|
||||
* If username field is visible, it will be taken from that, otherwise from the "name" property of the currentIndex
|
||||
*/
|
||||
signal passwordResult(string password)
|
||||
|
||||
onUserSelected: {
|
||||
const nextControl = (passwordBox.visible ? passwordBox : loginButton);
|
||||
// Don't startLogin() here, because the signal is connected to the
|
||||
// Escape key as well, for which it wouldn't make sense to trigger
|
||||
// login. Using TabFocusReason, so that the loginButton gets the
|
||||
// visual highlight.
|
||||
nextControl.forceActiveFocus(Qt.TabFocusReason);
|
||||
}
|
||||
|
||||
function startLogin() {
|
||||
const password = passwordBox.text
|
||||
|
||||
// This is partly because it looks nicer, but more importantly it
|
||||
// works round a Qt bug that can trigger if the app is closed with a
|
||||
// TextField focused.
|
||||
//
|
||||
// See https://bugreports.qt.io/browse/QTBUG-55460
|
||||
loginButton.forceActiveFocus();
|
||||
passwordResult(password);
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
PlasmaExtras.PasswordField {
|
||||
id: passwordBox
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize + 1
|
||||
Layout.fillWidth: true
|
||||
text: PasswordSync.password
|
||||
|
||||
placeholderText: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:placeholder in text field", "Password")
|
||||
focus: true
|
||||
enabled: !authenticator.graceLocked
|
||||
|
||||
// In Qt this is implicitly active based on focus rather than visibility
|
||||
// in any other application having a focussed invisible object would be weird
|
||||
// but here we are using to wake out of screensaver mode
|
||||
// We need to explicitly disable cursor flashing to avoid unnecessary renders
|
||||
cursorVisible: visible
|
||||
|
||||
onAccepted: {
|
||||
if (sessionManager.lockScreenUiVisible) {
|
||||
sessionManager.startLogin();
|
||||
}
|
||||
}
|
||||
|
||||
//if empty and left or right is pressed change selection in user switch
|
||||
//this cannot be in keys.onLeftPressed as then it doesn't reach the password box
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Left && !text) {
|
||||
sessionManager.userList.decrementCurrentIndex();
|
||||
event.accepted = true
|
||||
}
|
||||
if (event.key === Qt.Key_Right && !text) {
|
||||
sessionManager.userList.incrementCurrentIndex();
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onClearPassword() {
|
||||
passwordBox.forceActiveFocus()
|
||||
passwordBox.text = "";
|
||||
passwordBox.text = Qt.binding(() => PasswordSync.password);
|
||||
}
|
||||
function onNotificationRepeated() {
|
||||
sessionManager.playHighlightAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
Binding {
|
||||
target: PasswordSync
|
||||
property: "password"
|
||||
value: passwordBox.text
|
||||
}
|
||||
|
||||
PlasmaComponents3.Button {
|
||||
id: loginButton
|
||||
Accessible.name: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button accessible only", "Unlock")
|
||||
Layout.preferredHeight: passwordBox.implicitHeight
|
||||
Layout.preferredWidth: loginButton.Layout.preferredHeight
|
||||
|
||||
icon.name: LayoutMirroring.enabled ? "go-previous" : "go-next"
|
||||
|
||||
onClicked: sessionManager.startLogin()
|
||||
Keys.onEnterPressed: clicked()
|
||||
Keys.onReturnPressed: clicked()
|
||||
}
|
||||
}
|
||||
|
||||
component FailableLabel : PlasmaComponents3.Label {
|
||||
id: _failableLabel
|
||||
required property int kind
|
||||
required property string label
|
||||
|
||||
visible: authenticator.authenticatorTypes & kind
|
||||
text: label
|
||||
textFormat: Text.PlainText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
RejectPasswordAnimation {
|
||||
id: _rejectAnimation
|
||||
target: _failableLabel
|
||||
onFinished: _timer.restart()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: authenticator
|
||||
function onNoninteractiveError(kind, authenticator) {
|
||||
if (kind & _failableLabel.kind) {
|
||||
_failableLabel.text = Qt.binding(() => authenticator.errorMessage)
|
||||
_rejectAnimation.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: _timer
|
||||
interval: Kirigami.Units.humanMoment
|
||||
onTriggered: {
|
||||
_failableLabel.text = Qt.binding(() => _failableLabel.label)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FailableLabel {
|
||||
kind: ScreenLocker.Authenticator.Fingerprint
|
||||
label: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:usagetip", "(or scan your fingerprint on the reader)")
|
||||
}
|
||||
FailableLabel {
|
||||
kind: ScreenLocker.Authenticator.Smartcard
|
||||
label: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:usagetip", "(or scan your smartcard)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.components as PlasmaComponents3
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.private.mpris as Mpris
|
||||
|
||||
Item {
|
||||
visible: instantiator.count > 0
|
||||
implicitHeight: Kirigami.Units.gridUnit * 3
|
||||
implicitWidth: Kirigami.Units.gridUnit * 16
|
||||
|
||||
Repeater {
|
||||
id: instantiator
|
||||
model: Mpris.MultiplexerModel { }
|
||||
|
||||
RowLayout {
|
||||
id: controlsRow
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
enabled: model.canControl
|
||||
|
||||
Image {
|
||||
id: albumArt
|
||||
Layout.preferredWidth: height
|
||||
Layout.fillHeight: true
|
||||
visible: status === Image.Loading || status === Image.Ready
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: model.artUrl
|
||||
sourceSize.height: height * Screen.devicePixelRatio
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
implicitWidth: Kirigami.Units.smallSpacing
|
||||
implicitHeight: 1
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
PlasmaComponents3.Label {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize + 1
|
||||
maximumLineCount: 1
|
||||
text: model.track.length > 0
|
||||
? model.track
|
||||
: (model.playbackStatus > Mpris.PlaybackStatus.Stopped
|
||||
? i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:status", "No title")
|
||||
: i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:status", "No media playing"))
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
PlasmaExtras.DescriptiveLabel {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.NoWrap
|
||||
elide: Text.ElideRight
|
||||
// if no artist is given, show player name instead
|
||||
text: model.artist || model.identity
|
||||
textFormat: Text.PlainText
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize + 1
|
||||
maximumLineCount: 1
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaComponents3.ToolButton {
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredWidth: Layout.preferredHeight
|
||||
visible: model.canGoBack || model.canGoNext
|
||||
enabled: model.canGoPrevious
|
||||
focusPolicy: Qt.TabFocus
|
||||
icon.name: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward"
|
||||
onClicked: {
|
||||
fadeoutTimer.running = false
|
||||
model.container.Previous()
|
||||
}
|
||||
Accessible.name: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Accessible only", "Previous track")
|
||||
}
|
||||
|
||||
PlasmaComponents3.ToolButton {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: height // make this button bigger
|
||||
focusPolicy: Qt.TabFocus
|
||||
icon.name: model.playbackStatus === Mpris.PlaybackStatus.Playing ? "media-playback-pause" : "media-playback-start"
|
||||
onClicked: {
|
||||
fadeoutTimer.running = false
|
||||
model.container.PlayPause()
|
||||
}
|
||||
Accessible.name: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Accessible only", "Play or Pause media")
|
||||
}
|
||||
|
||||
PlasmaComponents3.ToolButton {
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredWidth: Layout.preferredHeight
|
||||
visible: model.canGoBack || model.canGoNext
|
||||
enabled: model.canGoNext
|
||||
focusPolicy: Qt.TabFocus
|
||||
icon.name: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward"
|
||||
Accessible.name: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button Accessible only", "Next track")
|
||||
onClicked: {
|
||||
fadeoutTimer.running = false
|
||||
model.container.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
import QtQuick
|
||||
|
||||
import org.kde.plasma.components as PlasmaComponents3
|
||||
|
||||
import org.kde.breeze.components
|
||||
|
||||
SessionManagementScreen {
|
||||
focus: true
|
||||
PlasmaComponents3.Button {
|
||||
id: loginButton
|
||||
focus: true
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button no-password unlock", "Unlock")
|
||||
icon.name: "unlock"
|
||||
onClicked: Qt.quit();
|
||||
Keys.onEnterPressed: clicked()
|
||||
Keys.onReturnPressed: clicked()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2025 Yifan Zhu <fanzhuyifan@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
property string password
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kcmutils as KCM
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: configForm
|
||||
|
||||
// TODO Plasma 7: Make this an enum.
|
||||
property bool cfg_alwaysShowClock
|
||||
property bool cfg_hideClockWhenIdle
|
||||
property bool cfg_alwaysShowClockDefault: true
|
||||
property bool cfg_hideClockWhenIdleDefault: false
|
||||
|
||||
property alias cfg_showMediaControls: showMediaControls.checked
|
||||
property bool cfg_showMediaControlsDefault: false
|
||||
|
||||
twinFormLayouts: parentLayout
|
||||
|
||||
QQC2.RadioButton {
|
||||
Kirigami.FormData.label: i18ndc("plasma_shell_org.kde.plasma.desktop",
|
||||
"@title: group",
|
||||
"Show clock:")
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@option:radio Clock always shown", "Always")
|
||||
Accessible.name: i18nc("@option:radio", "Always show clock")
|
||||
checked: configForm.cfg_alwaysShowClock && !configForm.cfg_hideClockWhenIdle
|
||||
onToggled: {
|
||||
configForm.cfg_alwaysShowClock = true;
|
||||
configForm.cfg_hideClockWhenIdle = false;
|
||||
}
|
||||
|
||||
KCM.SettingHighlighter {
|
||||
id: clockAlwaysHighlighter
|
||||
highlight: configForm.cfg_alwaysShowClock != configForm.cfg_alwaysShowClockDefault
|
||||
|| configForm.cfg_hideClockWhenIdle != configForm.cfg_hideClockWhenIdleDefault
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.RadioButton {
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@option:radio Clock shown only while unlock prompt is visible", "On unlocking prompt")
|
||||
Accessible.name: i18nc("@option:radio", "Show clock only on unlocking prompt")
|
||||
checked: configForm.cfg_alwaysShowClock && configForm.cfg_hideClockWhenIdle
|
||||
onToggled: {
|
||||
configForm.cfg_alwaysShowClock = true;
|
||||
configForm.cfg_hideClockWhenIdle = true;
|
||||
}
|
||||
|
||||
KCM.SettingHighlighter {
|
||||
highlight: clockAlwaysHighlighter.highlight
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.RadioButton {
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@option:radio Clock never shown", "Never")
|
||||
Accessible.name: i18nc("@option:radio", "Never show clock")
|
||||
checked: !configForm.cfg_alwaysShowClock
|
||||
onToggled: {
|
||||
configForm.cfg_alwaysShowClock = false;
|
||||
}
|
||||
|
||||
KCM.SettingHighlighter {
|
||||
highlight: clockAlwaysHighlighter.highlight
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.CheckBox {
|
||||
id: showMediaControls
|
||||
Kirigami.FormData.label: i18ndc("plasma_shell_org.kde.plasma.desktop",
|
||||
"@title: group UI controls for playback of multimedia content",
|
||||
"Media controls:")
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop",
|
||||
"@option:check",
|
||||
"Show under unlocking prompt")
|
||||
|
||||
KCM.SettingHighlighter {
|
||||
highlight: configForm.cfg_showMediaControlsDefault != configForm.cfg_showMediaControls
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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" >
|
||||
<kcfgfile name=""/>
|
||||
|
||||
<group name="General">
|
||||
<!-- TODO Plasma 7: Make this an enum. -->
|
||||
<entry name="alwaysShowClock" type="Bool">
|
||||
<label>Show a clock.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="hideClockWhenIdle" type="Bool">
|
||||
<label>Hide clock when prompt is hidden.</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="showMediaControls" type="Bool">
|
||||
<label>If true, shows any currently playing media along with controls to pause it.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
</group>
|
||||
|
||||
</kcfg>
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2025 Yifan Zhu <fanzhuyifan@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
singleton PasswordSync 1.0 PasswordSync.qml
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
loadTemplate("org.kde.plasma.desktop.defaultPanel")
|
||||
|
||||
var desktopsArray = desktopsForActivity(currentActivity());
|
||||
for( var j = 0; j < desktopsArray.length; j++) {
|
||||
desktopsArray[j].wallpaperPlugin = 'org.kde.image';
|
||||
//var clock = desktopsArray[j].addWidget("org.kde.plasma.analogclock");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
00-start-here-kde-fedora.js - Set launcher icon to start-here-kde-fedora
|
||||
Copyright (C) 2010 Kevin Kofler <kevin.kofler@chello.at>
|
||||
Copyright (C) 2010 Rex Dieter <rdieter@fedoraproject.org>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Portions lifted from 01-kubuntu-10.04.js:
|
||||
Harald Sitter, apachelogger@ubuntu.com 2010-04-02
|
||||
Jonathan Riddell, jriddell@ubuntu.com 2010-02-18
|
||||
Copyright Canonical Ltd, may be copied under the GNU GPL 2 or later
|
||||
*/
|
||||
|
||||
launcherFound = false;
|
||||
|
||||
pids = panelIds;
|
||||
for (i = 0; i < pids.length; ++i) {
|
||||
p = panelById(pids[i]);
|
||||
if (!p) continue;
|
||||
ids = p.widgetIds;
|
||||
for (j = 0; j < ids.length; ++j) {
|
||||
w = p.widgetById(ids[j]);
|
||||
if (!w) continue;
|
||||
if ( w.type != "org.kde.plasma.kickoff" &&
|
||||
w.type != "org.kde.plasma.kicker" &&
|
||||
w.type != "org.kde.plasma.kickerdash" )
|
||||
continue;
|
||||
launcherFound = true;
|
||||
if ( w.readConfig("icon", "start-here-kde") == "start-here-kde-fedora" ) {
|
||||
w.currentConfigGroup = ["General"];
|
||||
w.writeConfig("icon", "start-here");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (launcherFound) break;
|
||||
}
|
||||
if (!launcherFound)
|
||||
print("No launcher found");
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
const allPanels = panels();
|
||||
|
||||
for (let i = 0; i < allPanels.length; ++i) {
|
||||
const panel = allPanels[i];
|
||||
const widgets = panel.widgets();
|
||||
|
||||
for (let j = 0; j < widgets.length; ++j) {
|
||||
const widget = widgets[j];
|
||||
|
||||
if (widget.type === "org.kde.plasma.icontasks") {
|
||||
widget.currentConfigGroup = ["General"];
|
||||
|
||||
// Read the current launchers value
|
||||
const currentLaunchers = widget.readConfig("launchers", "");
|
||||
|
||||
// Only set our default if launchers is empty
|
||||
if (!currentLaunchers || currentLaunchers.trim() === "") {
|
||||
widget.writeConfig("launchers", [
|
||||
"preferred://browser",
|
||||
"applications:steam.desktop",
|
||||
"applications:net.lutris.Lutris.desktop",
|
||||
"applications:org.kde.konsole.desktop",
|
||||
"applications:io.github.kolunmi.Bazaar.desktop",
|
||||
"preferred://filemanager"
|
||||
]);
|
||||
widget.reloadConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// MidButton got deprecated and doesn't really work anymore as a stringified enum value
|
||||
// for the middle button Qt::MouseButton, we need to update our config to "MiddleButton"
|
||||
var plasmaConfig = ConfigFile("plasma-org.kde.plasma.desktop-appletsrc", "ActionPlugins");
|
||||
|
||||
for (let i in plasmaConfig.groupList) {
|
||||
let subGroup = ConfigFile(plasmaConfig, plasmaConfig.groupList[i])
|
||||
|
||||
for (let j in subGroup.keyList) {
|
||||
let key = subGroup.keyList[j];
|
||||
if (key.indexOf("MidButton") !== -1) {
|
||||
subGroup.writeEntry(key.replace("MidButton", "MiddleButton"), subGroup.readEntry(key));
|
||||
subGroup.deleteEntry(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Jin Liu <ad.liu.jin@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
/*
|
||||
Plasma 5.26 introduced a new config entry autoFontAndSize which defaults to true.
|
||||
This means if the user customized font before (fontFamily, boldText, italicText),
|
||||
in 5.26 these settings are ignored.
|
||||
|
||||
So we need to set autoFontAndSize=false if:
|
||||
1. Any of these 3 old entries above is set.
|
||||
2. No new entries introduced in 5.26 (autoFontAndSize, fontSize, fontWeight, fontStyleName)
|
||||
are set, so this is a config from 5.25.
|
||||
|
||||
And fontWeight should be set to 75 (Font.Bold) if boldText==true.
|
||||
|
||||
See https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/1809
|
||||
*/
|
||||
|
||||
const containments = desktops().concat(panels());
|
||||
for (var i in containments) {
|
||||
var cont = containments[i];
|
||||
const widgets = cont.widgets();
|
||||
for (var j in widgets) {
|
||||
var widget = widgets[j];
|
||||
|
||||
if (widget.type == "org.kde.plasma.digitalclock") {
|
||||
widget.currentConfigGroup = new Array('Appearance')
|
||||
if ((widget.readConfig("fontFamily", "").length > 0
|
||||
|| widget.readConfig("boldText", false)
|
||||
|| widget.readConfig("italicText", false))
|
||||
&&
|
||||
(widget.readConfig("autoFontAndSize", true)
|
||||
&& widget.readConfig("fontSize", 10) === 10
|
||||
&& widget.readConfig("fontWeight", 50) === 50
|
||||
&& widget.readConfig("fontStyleName", "").length === 0)) {
|
||||
widget.writeConfig("autoFontAndSize", false)
|
||||
if (widget.readConfig("boldText", false)) {
|
||||
widget.writeConfig("fontWeight", 75)
|
||||
}
|
||||
// Set the font size to the largest value (72) in the font dialog,
|
||||
// so the font autofits the panel when the panel height is less
|
||||
// than 72pt. This should keep 5.25's autosize behavior for custom
|
||||
// font.
|
||||
// For panels taller than 72pt, with custom font set in 5.25, the
|
||||
// digital clock's look may still change, though.
|
||||
widget.writeConfig("fontSize", 72)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
/**
|
||||
@c showSeconds option now supports showing seconds only in the tooltip.
|
||||
|
||||
@see https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2232
|
||||
@since 6.0
|
||||
*/
|
||||
|
||||
const containments = desktops().concat(panels());
|
||||
|
||||
containments.forEach(containment => containment.widgets("org.kde.plasma.digitalclock").forEach(widget => {
|
||||
widget.currentConfigGroup = ["Appearance"];
|
||||
if (widget.readConfig("showSeconds", false /* Default: never show seconds */) === true /* Changed by the user */) {
|
||||
widget.writeConfig("showSeconds", 2 /* Always show seconds */);
|
||||
}
|
||||
}));
|
||||
@@ -0,0 +1,23 @@
|
||||
// Find all digital clock applets in all containments and change
|
||||
// displayTimezoneAsCode=false
|
||||
// to
|
||||
// displayTimezoneFormat=FullText
|
||||
// See https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/751
|
||||
|
||||
var containments = desktops().concat(panels());
|
||||
for (var i in containments) {
|
||||
var cont = containments[i];
|
||||
|
||||
for (var j in cont.widgetIds) {
|
||||
var widget = cont.widgetById(cont.widgetIds[j]);
|
||||
|
||||
if (widget.type == "org.kde.plasma.digitalclock") {
|
||||
widget.currentConfigGroup = new Array('Appearance')
|
||||
if (widget.readConfig("displayTimezoneAsCode", true) == false) {
|
||||
widget.writeConfig("displayTimezoneFormat", "FullText")
|
||||
// Work around not being able to delete config file keys using widget interface
|
||||
widget.writeConfig("displayTimezoneAsCode", "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/* vim:set foldmethod=marker:
|
||||
|
||||
SPDX-FileCopyrightText: 2023 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
function filterDisabled(entries) {
|
||||
let filteredEntries = [];
|
||||
|
||||
// 0 = screen, 1 = activity, 2 = how many entries, 3 = desktop entry
|
||||
let state = 0;
|
||||
let entriesForCurrentScreen = 0;
|
||||
|
||||
let currentScreen = -1;
|
||||
let currentActivity = "";
|
||||
let currentEntrtriesNumber = 0;
|
||||
let currentEntry = 0;
|
||||
let currentEntries = [];
|
||||
|
||||
for (let e of entries) {
|
||||
switch (state) {
|
||||
case 0: // Screen
|
||||
currentScreen = e;
|
||||
state = 1;
|
||||
break;
|
||||
case 1: // Activity
|
||||
currentActivity = e;
|
||||
state = 2;
|
||||
break;
|
||||
case 2: // Entries number
|
||||
currentEntrtriesNumber = Number(e);
|
||||
state = 3;
|
||||
break;
|
||||
case 3: // Desktop file
|
||||
if (e.indexOf("desktop:/") !== 0) { // User has a folderview not in desktop:/
|
||||
currentEntries.push(e);
|
||||
currentEntry++;
|
||||
} else {
|
||||
let count = (e.match(/\//g) || []).length;
|
||||
if (count == 1) {
|
||||
currentEntries.push(e);
|
||||
currentEntry++;
|
||||
} else {
|
||||
currentEntrtriesNumber--;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentEntry === currentEntrtriesNumber) {
|
||||
state = 0;
|
||||
if (currentEntries.length > 0) {
|
||||
filteredEntries = filteredEntries.concat([currentScreen, currentActivity, currentEntrtriesNumber]);
|
||||
filteredEntries = filteredEntries.concat(currentEntries);
|
||||
currentEntries = [];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return filteredEntries;
|
||||
}
|
||||
|
||||
function filterEnabled(entries) {
|
||||
let filteredEntries = [];
|
||||
|
||||
// 0 = desktop entry, 1 = screen 2 = activity
|
||||
let state = 0;
|
||||
let shouldDrop = false; //true when this entry should be dropped
|
||||
|
||||
for (let e of entries) {
|
||||
switch (state) {
|
||||
case 0: // Desktop file
|
||||
if (e.indexOf("desktop:/") !== 0) { // User has a folderview not in desktop:/
|
||||
filteredEntries.push(e);
|
||||
shouldDrop = false;
|
||||
} else {
|
||||
let count = (e.match(/\//g) || []).length;
|
||||
if (count == 1) {
|
||||
filteredEntries.push(e);
|
||||
shouldDrop = false;
|
||||
} else {
|
||||
shouldDrop = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1: // Screen
|
||||
case 2: // Activity
|
||||
if (!shouldDrop) {
|
||||
filteredEntries.push(e);
|
||||
}
|
||||
}
|
||||
state = (state + 1) % 3;
|
||||
}
|
||||
return filteredEntries;
|
||||
}
|
||||
|
||||
const config = ConfigFile('plasma-org.kde.plasma.desktop-appletsrc');
|
||||
config.group = 'ScreenMapping';
|
||||
|
||||
let entries = config.readEntry("itemsOnDisabledScreens").split(",");
|
||||
let filteredEntries = filterDisabled(entries);
|
||||
|
||||
config.writeEntry("itemsOnDisabledScreens", filteredEntries.join(","));
|
||||
|
||||
entries = config.readEntry("screenMapping").split(",");
|
||||
filteredEntries = filterEnabled(entries);
|
||||
|
||||
config.writeEntry("screenMapping", filteredEntries.join(","));
|
||||
@@ -0,0 +1,33 @@
|
||||
// Find all Keyboard Layout applets in all containments and change
|
||||
// showFlag=true
|
||||
// to
|
||||
// displayStyle=Flag
|
||||
// See https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/1131
|
||||
|
||||
const containments = desktops().concat(panels());
|
||||
for (var i in containments) {
|
||||
forEachWidgetInContainment(containments[i]);
|
||||
}
|
||||
|
||||
function forEachWidgetInContainment(containment) {
|
||||
const widgets = containment.widgets();
|
||||
for (var i in widgets) {
|
||||
const widget = widgets[i];
|
||||
switch(widget.type) {
|
||||
case "org.kde.plasma.systemtray":
|
||||
systemtrayId = widget.readConfig("SystrayContainmentId");
|
||||
if (systemtrayId) {
|
||||
forEachWidgetInContainment(desktopById(systemtrayId))
|
||||
}
|
||||
break;
|
||||
case "org.kde.plasma.keyboardlayout":
|
||||
widget.currentConfigGroup = new Array('General')
|
||||
if (widget.readConfig("showFlag", false) == true) {
|
||||
widget.writeConfig("displayStyle", "Flag")
|
||||
// Work around not being able to delete config file keys using widget interface
|
||||
widget.writeConfig("showFlag", "")
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
const containments = desktops().concat(panels());
|
||||
for (var i in containments) {
|
||||
forEachWidgetInContainment(containments[i]);
|
||||
}
|
||||
|
||||
function forEachWidgetInContainment(containment) {
|
||||
const widgets = containment.widgets();
|
||||
for (var i in widgets) {
|
||||
const widget = widgets[i];
|
||||
switch(widget.type) {
|
||||
case "org.kde.plasma.systemtray":
|
||||
systemtrayId = widget.readConfig("SystrayContainmentId");
|
||||
if (systemtrayId) {
|
||||
forEachWidgetInContainment(desktopById(systemtrayId))
|
||||
}
|
||||
break;
|
||||
case "org.kde.plasma.keyboardlayout":
|
||||
if (widget.globalShortcut) {
|
||||
print("Shortcut to remove: " + widget.globalShortcut);
|
||||
widget.globalShortcut = "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Previously, Klipper "clear history" dialog used to not ask again even if user answered No.
|
||||
This was changed so that only a Yes answer will lead to no more asking, by using warningContinueCancel instead of questionYesNo.
|
||||
Now the behaviour of previous config value really_clear_history has inverted: true/undefined => ask again; false => don't ask.
|
||||
This update script migrates old configs to use the new config value, renamed to klipperClearHistoryAskAgain.
|
||||
*/
|
||||
|
||||
config = ConfigFile("plasmashellrc", "Notification Messages");
|
||||
oldVal = config.readEntry("really_clear_history");
|
||||
if (oldVal === "true") {
|
||||
// Clear and don't ask again -- preserve this choice
|
||||
config.writeEntry("klipperClearHistoryAskAgain", false)
|
||||
}
|
||||
config.deleteEntry("really_clear_history");
|
||||
@@ -0,0 +1,46 @@
|
||||
// This script updates users' Folder View icon sizes following a change in what
|
||||
// they mean in https://invent.kde.org/plasma/plasma-desktop/-/merge_requests/111
|
||||
|
||||
var allDesktops = desktops();
|
||||
|
||||
for (var i = 0; i < allDesktops.length; ++i) {
|
||||
var desktop = allDesktops[i];
|
||||
desktop.currentConfigGroup = ["General"];
|
||||
|
||||
var currentIconSize = desktop.readConfig("iconSize");
|
||||
|
||||
// Don't do anything if there is no value in the config file, since in this
|
||||
// case, no change is needed because the new default works properly
|
||||
if (currentIconSize) {
|
||||
currentIconSize = parseInt(currentIconSize);
|
||||
|
||||
// No change needed for iconSize=0 or 5
|
||||
if (currentIconSize != 0 && currentIconSize != 5) {
|
||||
print("Current icon size is " + currentIconSize);
|
||||
var newIconSize = 3;
|
||||
|
||||
switch(currentIconSize) {
|
||||
case 1:
|
||||
newIconSize = 0;
|
||||
break;
|
||||
case 2:
|
||||
newIconSize = 1;
|
||||
break;
|
||||
case 3:
|
||||
newIconSize = 2;
|
||||
break;
|
||||
case 4:
|
||||
newIconSize = 3;
|
||||
break;
|
||||
// We should never reach here, but in case we do anyway, reset to
|
||||
// the default value
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
desktop.writeConfig("iconSize", newIconSize);
|
||||
desktop.reloadConfig()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Fushan Wen <qydwhotmail@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
/**
|
||||
The Media Frame widget removes the useBackground option and uses ConfigurableBackground
|
||||
hint to support toggling the background directly from the widget toolbar.
|
||||
|
||||
The option only applies to media frame widgets on desktop.
|
||||
|
||||
@see https://invent.kde.org/plasma/kdeplasma-addons/-/merge_requests/238
|
||||
@since 5.27
|
||||
*/
|
||||
desktops().forEach(containment => containment.widgets("org.kde.plasma.mediaframe").forEach(widget => {
|
||||
widget.currentConfigGroup = ["General"];
|
||||
if (widget.readConfig("useBackground", true /* Default */) === false /* Changed by the user */) {
|
||||
widget.writeConfig("useBackground", "");
|
||||
widget.currentConfigGroup = []; // Root Configuration
|
||||
widget.writeConfig("UserBackgroundHints", "NoBackground");
|
||||
}
|
||||
}));
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Akseli Lahtinen <akselmo@akselmo.dev>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Find all depicted widgets and migrate their font weights from qt5 to qt6 style
|
||||
|
||||
var containments = desktops().concat(panels());
|
||||
for (var c in containments) {
|
||||
const cont = containments[c];
|
||||
const widgets = cont.widgets();
|
||||
for (var w in widgets) {
|
||||
var widget = widgets[w];
|
||||
switch(widget.type) {
|
||||
case "org.kde.plasma.digitalclock":
|
||||
widget.currentConfigGroup = ['Appearance'];
|
||||
// Use "normal" weight as default if weight is not set
|
||||
const oldFontWeight = widget.readConfig("fontWeight", 400);
|
||||
const newFontWeight = migrateFontWeight(Number(oldFontWeight));
|
||||
widget.writeConfig("fontWeight", newFontWeight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function migrateFontWeight(oldWeight) {
|
||||
// Takes old weight (Qt5 weight) and returns the Qt6 equivalent
|
||||
// Qt5 font weights: https://doc.qt.io/qt-5/qfont.html#Weight-enum
|
||||
// Qt6 font weights: https://doc.qt.io/qt-6/qfont.html#Weight-enum
|
||||
var newWeight = 400;
|
||||
if (oldWeight === 0) { newWeight = 100; }
|
||||
else if (oldWeight === 12) { newWeight = 200; }
|
||||
else if (oldWeight === 25) { newWeight = 300; }
|
||||
else if (oldWeight === 50) { newWeight = 400; }
|
||||
else if (oldWeight === 57) { newWeight = 500; }
|
||||
else if (oldWeight === 63) { newWeight = 600; }
|
||||
else if (oldWeight === 75) { newWeight = 700; }
|
||||
else if (oldWeight === 81) { newWeight = 800; }
|
||||
else if (oldWeight === 87) { newWeight = 900; }
|
||||
|
||||
return newWeight;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
var allDesktops = desktops();
|
||||
|
||||
for (var i = 0; i < allDesktops.length; ++i) {
|
||||
var desktop = allDesktops[i];
|
||||
desktop.currentConfigGroup = ["General"];
|
||||
var serializedItems = desktop.readConfig("ItemsGeometries");
|
||||
desktop.currentConfigGroup = [];
|
||||
desktop.writeConfig("ItemGeometriesHorizontal", serializedItems);
|
||||
desktop.reloadConfig()
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// In the past, panels were configured to add a note on middle-click. This was changed,
|
||||
// but the "MiddleButton;NoModifier=org.kde.paste" action was never removed from the
|
||||
// config file, so some people still got this undesirable behavior with no GIU method
|
||||
// to change it.
|
||||
//
|
||||
// This script removes it.
|
||||
|
||||
var plasmaConfig = ConfigFile("plasma-org.kde.plasma.desktop-appletsrc", "ActionPlugins");
|
||||
|
||||
for (let i in plasmaConfig.groupList) {
|
||||
let subSubGroupKeys = [];
|
||||
let subGroup = ConfigFile(plasmaConfig, plasmaConfig.groupList[i]);
|
||||
for (let j in subGroup.groupList) {
|
||||
let subSubGroup = ConfigFile(subGroup, subGroup.groupList[j]);
|
||||
subSubGroupKeys = subSubGroup.keyList;
|
||||
}
|
||||
if (subSubGroupKeys.indexOf("_sep1") === -1) {
|
||||
print("Containment " + i + " Does not have a _sep1 item; it must be a panel.\n");
|
||||
// No _sep1 item; this must be a panel
|
||||
let mmbAction = subGroup.readEntry("MiddleButton;NoModifier");
|
||||
if (mmbAction === "org.kde.paste") {
|
||||
print("Panel " + i + " Seems to have a middle-click paste action defined; deleting it!");
|
||||
subGroup.deleteEntry("MiddleButton;NoModifier");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
function swapWidget(cont, oldWidget, newType, geometry) {
|
||||
oldWidget.remove();
|
||||
cont.addWidget(newType, geometry.x, geometry.y, geometry.width, geometry.height);
|
||||
}
|
||||
|
||||
var containments = desktops().concat(panels());
|
||||
|
||||
for (var i in containments) {
|
||||
var cont = containments[i];
|
||||
|
||||
for (var j in cont.widgetIds) {
|
||||
var widget = cont.widgetById(cont.widgetIds[j]);
|
||||
|
||||
let newType = ""
|
||||
if (widget.type == "org.kde.plasma.systemloadviewer") {
|
||||
let geometry = widget.geometry;
|
||||
geometry.width = geometry.width/3
|
||||
|
||||
widget.remove();
|
||||
cont.addWidget("org.kde.plasma.systemmonitor.cpuusage", geometry.x, geometry.y, geometry.width, geometry.height);
|
||||
geometry.x += geometry.width;
|
||||
cont.addWidget("org.kde.plasma.systemmonitor.memoryusage", geometry.x, geometry.y, geometry.width, geometry.height);
|
||||
geometry.x += geometry.width;
|
||||
|
||||
let swapWidget = cont.addWidget("org.kde.plasma.systemmonitor", geometry.x, geometry.y, geometry.width, geometry.height);
|
||||
swapWidget.currentConfigGroup = ["Appearance"];
|
||||
swapWidget.writeConfig("title", "Swap");
|
||||
swapWidget.currentConfigGroup = ["Sensors"];
|
||||
swapWidget.writeConfig("highPrioritySensorIds", "[\"mem/swap/used\",\"mem/swap/free\"]");
|
||||
swapWidget.writeConfig("totalSensors", "[\"mem/swap/used\"]");
|
||||
swapWidget.currentConfigGroup = ["SensorColors"];
|
||||
swapWidget.writeConfig("mem/swap/free", "230,230,230");
|
||||
|
||||
swapWidget.reloadconfiguration();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Update all applets with wheelEnabled=true to wheelEnabled=AllTask
|
||||
|
||||
var containments = desktops().concat(panels());
|
||||
|
||||
containments.forEach(function(cont) {
|
||||
|
||||
cont.widgetIds.forEach(function(id) {
|
||||
var widget = cont.widgetById(id);
|
||||
|
||||
widget.currentConfigGroup = new Array("General");
|
||||
|
||||
if (widget.readConfig("wheelEnabled", false) === true) {
|
||||
widget.writeConfig("wheelEnabled", "AllTask");
|
||||
widget.reloadConfig();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
__AppInterface.locked = false;
|
||||
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org>
|
||||
SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.kwindowsystem
|
||||
import org.kde.plasma.activityswitcher as ActivitySwitcher
|
||||
import "../activitymanager"
|
||||
import "../explorer"
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property Item containment
|
||||
|
||||
property QtObject widgetExplorer
|
||||
|
||||
Connections {
|
||||
target: ActivitySwitcher.Backend
|
||||
function onShouldShowSwitcherChanged(): void {
|
||||
if (ActivitySwitcher.Backend.shouldShowSwitcher) {
|
||||
if (sidePanelStack.state !== "activityManager") {
|
||||
root.toggleActivityManager();
|
||||
}
|
||||
|
||||
} else {
|
||||
if (sidePanelStack.state === "activityManager") {
|
||||
root.toggleActivityManager();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleWidgetExplorer(containment) {
|
||||
|
||||
if (sidePanelStack.state === "widgetExplorer") {
|
||||
sidePanelStack.state = "closed";
|
||||
} else {
|
||||
sidePanelStack.state = "widgetExplorer";
|
||||
sidePanelStack.setSource(Qt.resolvedUrl("../explorer/WidgetExplorer.qml"), {
|
||||
containment,
|
||||
sidePanel,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toggleActivityManager() {
|
||||
if (sidePanelStack.state === "activityManager") {
|
||||
sidePanelStack.state = "closed";
|
||||
} else {
|
||||
sidePanelStack.state = "activityManager";
|
||||
sidePanelStack.setSource(Qt.resolvedUrl("../activitymanager/ActivityManager.qml"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
readonly property rect editModeRect: {
|
||||
if (!containment) {
|
||||
return Qt.rect(0,0,0,0);
|
||||
}
|
||||
let screenRect = desktop.strictAvailableScreenRect;
|
||||
let panelConfigRect = Qt.rect(0,0,0,0);
|
||||
|
||||
if (containment.plasmoid.corona.panelBeingConfigured
|
||||
&& containment.plasmoid.corona.panelBeingConfigured.screenToFollow === desktop.screenToFollow) {
|
||||
panelConfigRect = containment.plasmoid.corona.panelBeingConfigured.relativeConfigRect;
|
||||
}
|
||||
|
||||
if (panelConfigRect.width <= 0) {
|
||||
; // Do nothing
|
||||
} else if (panelConfigRect.x > width - (panelConfigRect.x + panelConfigRect.width)) {
|
||||
screenRect = Qt.rect(screenRect.x, screenRect.y, panelConfigRect.x - screenRect.x, screenRect.height);
|
||||
} else {
|
||||
const diff = Math.max(0, panelConfigRect.x + panelConfigRect.width - screenRect.x);
|
||||
screenRect = Qt.rect(Math.max(screenRect.x, panelConfigRect.x + panelConfigRect.width), screenRect.y, screenRect.width - diff, screenRect.height);
|
||||
}
|
||||
|
||||
if (sidePanel.visible) {
|
||||
if (sidePanel.sideBarOnRightEdge) {
|
||||
screenRect = Qt.rect(screenRect.x, screenRect.y, screenRect.width - sidePanel.width, screenRect.height);
|
||||
} else {
|
||||
screenRect = Qt.rect(screenRect.x + sidePanel.width, screenRect.y, screenRect.width - sidePanel.width, screenRect.height);
|
||||
}
|
||||
}
|
||||
return screenRect;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: desktopMouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: containment.plasmoid.corona.editMode = false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: containmentParent
|
||||
x: editModeLoader.item ? editModeLoader.item.centerX - width / 2 : 0
|
||||
y: editModeLoader.item ? editModeLoader.item.centerY - height / 2 : 0
|
||||
width: root.width
|
||||
height: root.height
|
||||
readonly property real extraScale: desktop.configuredPanel || sidePanel.visible ? 0.95 : 0.9
|
||||
property real scaleFactor: Math.min(editModeRect.width / root.width, editModeRect.height / root.height) * extraScale
|
||||
scale: containment?.plasmoid.corona.editMode ? scaleFactor : 1
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: editModeLoader
|
||||
anchors.fill: parent
|
||||
sourceComponent: DesktopEditMode {}
|
||||
active: containment?.plasmoid.corona.editMode || editModeUiTimer.running
|
||||
Timer {
|
||||
id: editModeUiTimer
|
||||
property bool editMode: containment?.plasmoid.corona.editMode ?? false
|
||||
onEditModeChanged: restart()
|
||||
interval: 200
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: wallpaperColors
|
||||
|
||||
active: root.containment && root.containment.wallpaper && desktop.usedInAccentColor
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: Kirigami.ImageColors {
|
||||
id: imageColors
|
||||
source: root.containment.wallpaper
|
||||
|
||||
readonly property color backgroundColor: Kirigami.Theme.backgroundColor
|
||||
readonly property color textColor: Kirigami.Theme.textColor
|
||||
|
||||
// Ignore the initial dominant color
|
||||
onPaletteChanged: {
|
||||
if (!Qt.colorEqual(root.containment.wallpaper.accentColor, "transparent")) {
|
||||
desktop.accentColor = root.containment.wallpaper.accentColor;
|
||||
}
|
||||
if (this.palette.length === 0) {
|
||||
desktop.accentColor = "transparent";
|
||||
} else {
|
||||
desktop.accentColor = this.dominant;
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.backgroundColor: backgroundColor
|
||||
Kirigami.Theme.textColor: textColor
|
||||
|
||||
onBackgroundColorChanged: Qt.callLater(update)
|
||||
onTextColorChanged: Qt.callLater(update)
|
||||
|
||||
readonly property Connections __repaintConnection: Connections {
|
||||
target: root.containment.wallpaper
|
||||
function onAccentColorChanged() {
|
||||
if (Qt.colorEqual(root.containment.wallpaper.accentColor, "transparent")) {
|
||||
imageColors.update();
|
||||
} else {
|
||||
imageColors.paletteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: pendingUninstallTimer
|
||||
// keeps track of the applets the user wants to uninstall
|
||||
property list<string> applets: []
|
||||
function uninstall() {
|
||||
for (const applet of applets) {
|
||||
widgetExplorer.uninstall(applet);
|
||||
}
|
||||
applets = [];
|
||||
|
||||
if (sidePanelStack.state !== "widgetExplorer" && widgetExplorer) {
|
||||
widgetExplorer.destroy();
|
||||
widgetExplorer = null;
|
||||
}
|
||||
}
|
||||
|
||||
interval: 60000 // one minute
|
||||
onTriggered: uninstall()
|
||||
}
|
||||
|
||||
PlasmaCore.Dialog {
|
||||
id: sidePanel
|
||||
|
||||
// If we are currently in edit mode, all panels are being shown
|
||||
// and we use the strictAvailableScreenRect, which accounts for all
|
||||
// of them. If we're not configuring anything, we instead use the
|
||||
// entire screen rect, without fear of overlapping panels.
|
||||
property var referenceRect: containment?.plasmoid.corona.editMode ? desktop.strictAvailableScreenRect : Qt.rect(0, 0, desktop.screenGeometry.width, desktop.screenGeometry.height)
|
||||
|
||||
|
||||
readonly property bool sideBarOnRightEdge: {
|
||||
if (!sidePanelStack.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const item = sidePanelStack.item;
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rightEdgeParent = (item.containment
|
||||
&& item.containment !== containment.plasmoid
|
||||
&& item.containment.location == PlasmaCore.Types.RightEdge);
|
||||
|
||||
return rightEdgeParent || Application.layoutDirection === Qt.RightToLeft;
|
||||
}
|
||||
|
||||
location: sideBarOnRightEdge ? PlasmaCore.Types.RightEdge : PlasmaCore.Types.LeftEdge
|
||||
type: PlasmaCore.Dialog.Dock
|
||||
flags: Qt.WindowStaysOnTopHint
|
||||
|
||||
hideOnWindowDeactivate: true
|
||||
|
||||
x: {
|
||||
let result = desktop.x;
|
||||
if (!containment) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const rect = referenceRect;
|
||||
result += rect.x;
|
||||
|
||||
if (sideBarOnRightEdge) {
|
||||
result += rect.width - sidePanel.width;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
y: desktop.y + (containment ? referenceRect.y : 0)
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
sidePanelStack.state = "closed";
|
||||
ActivitySwitcher.Backend.shouldShowSwitcher = false;
|
||||
}
|
||||
}
|
||||
|
||||
mainItem: Loader {
|
||||
id: sidePanelStack
|
||||
asynchronous: true
|
||||
width: item ? item.width : 0
|
||||
height: containment ? sidePanel.referenceRect.height - sidePanel.margins.top - sidePanel.margins.bottom : 1000
|
||||
state: "closed"
|
||||
|
||||
function bindingWithItem(callback: var, defaults: var): var {
|
||||
return Qt.binding(() => {
|
||||
const item = this.item;
|
||||
return item !== null ? callback(item) : defaults;
|
||||
});
|
||||
}
|
||||
|
||||
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
onLoaded: {
|
||||
if (item) {
|
||||
item.closed.connect(() => {
|
||||
state = "closed";
|
||||
});
|
||||
|
||||
switch (state) {
|
||||
case "activityManager":
|
||||
item.showSwitcherOnly = ActivitySwitcher.Backend.shouldShowSwitcher;
|
||||
sidePanel.hideOnWindowDeactivate = bindingWithItem(
|
||||
item => !ActivitySwitcher.Backend.shouldShowSwitcher && !item.showingDialog,
|
||||
false,
|
||||
);
|
||||
item.forceActiveFocus();
|
||||
break;
|
||||
case "widgetExplorer":
|
||||
sidePanel.hideOnWindowDeactivate = bindingWithItem(item => !item.preventWindowHide, false);
|
||||
sidePanel.opacity = bindingWithItem(item => item.opacity, 1);
|
||||
sidePanel.outputOnly = bindingWithItem(item => item.outputOnly, false);
|
||||
break;
|
||||
default:
|
||||
sidePanel.hideOnWindowDeactivate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sidePanel.visible = true;
|
||||
if (KWindowSystem.isPlatformX11) {
|
||||
KX11Extras.forceActiveWindow(sidePanel);
|
||||
}
|
||||
}
|
||||
onStateChanged: {
|
||||
if (state === "closed") {
|
||||
sidePanel.visible = false;
|
||||
source = ""; //unload all elements
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: desktop
|
||||
function onStrictAvailableScreenRectChanged() {
|
||||
if (sidePanel.visible) {
|
||||
sidePanel.requestActivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onContainmentChanged: {
|
||||
if (containment === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
containment.parent = containmentParent
|
||||
|
||||
if (switchAnim.running) {
|
||||
//If the animation was still running, stop it and reset
|
||||
//everything so that a consistent state can be kept
|
||||
switchAnim.running = false;
|
||||
internal.newContainment.visible = false;
|
||||
internal.oldContainment.visible = false;
|
||||
internal.oldContainment = null;
|
||||
}
|
||||
|
||||
internal.newContainment = containment;
|
||||
containment.visible = true;
|
||||
|
||||
if (internal.oldContainment !== null && internal.oldContainment !== containment) {
|
||||
switchAnim.running = true;
|
||||
} else {
|
||||
containment.anchors.left = containmentParent.left;
|
||||
containment.anchors.top = containmentParent.top;
|
||||
containment.anchors.right = containmentParent.right;
|
||||
containment.anchors.bottom = containmentParent.bottom;
|
||||
if (internal.oldContainment) {
|
||||
internal.oldContainment.visible = false;
|
||||
}
|
||||
internal.oldContainment = containment;
|
||||
}
|
||||
}
|
||||
|
||||
//some properties that shouldn't be accessible from elsewhere
|
||||
QtObject {
|
||||
id: internal
|
||||
|
||||
property Item oldContainment: null
|
||||
property Item newContainment: null
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: switchAnim
|
||||
ScriptAction {
|
||||
script: {
|
||||
if (containment) {
|
||||
containment.z = 1;
|
||||
containment.x = root.width;
|
||||
}
|
||||
if (internal.oldContainment) {
|
||||
internal.oldContainment.z = 0;
|
||||
internal.oldContainment.x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
target: internal.oldContainment
|
||||
properties: "x"
|
||||
to: internal.newContainment != null ? -root.width : 0
|
||||
duration: Kirigami.Units.veryLongDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
target: internal.newContainment
|
||||
properties: "x"
|
||||
to: 0
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
if (internal.oldContainment) {
|
||||
internal.oldContainment.visible = false;
|
||||
}
|
||||
if (containment) {
|
||||
internal.oldContainment = containment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: previewBannerLoader
|
||||
|
||||
function shouldBeActive(): bool {
|
||||
// Loader::active is true by default at the time of creation, so
|
||||
// it shouldn't be used in other bindings as a guard.
|
||||
return root.containment !== null && (desktop.showPreviewBanner ?? false);
|
||||
}
|
||||
|
||||
readonly property point pos: root.containment?.plasmoid.availableScreenRegion, shouldBeActive() && item !== null
|
||||
? root.containment.adjustToAvailableScreenRegion(
|
||||
root.containment.width + root.containment.x - item.width - Kirigami.Units.largeSpacing,
|
||||
root.containment.height + root.containment.y - item.height - Kirigami.Units.largeSpacing,
|
||||
item.width + Kirigami.Units.largeSpacing,
|
||||
item.height + Kirigami.Units.largeSpacing)
|
||||
: Qt.point(0, 0)
|
||||
|
||||
x: pos.x
|
||||
y: pos.y
|
||||
z: (root.containment?.z ?? 0) + 1
|
||||
active: shouldBeActive()
|
||||
visible: active
|
||||
source: "PreviewBanner.qml"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Marco Martin <mart@kde.org>
|
||||
SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.components as PC
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.kcmutils as KCM
|
||||
|
||||
Item {
|
||||
id: editModeItem
|
||||
property real centerX: Math.round(editModeUi.x + editModeUi.width/2)
|
||||
property real centerY: Math.round(editModeUi.y + editModeUi.height/2)
|
||||
property real roundedRootWidth: Math.round(root.width)
|
||||
property real roundedRootHeight: Math.round(root.height)
|
||||
|
||||
property bool open: false
|
||||
Component.onCompleted: {
|
||||
open = Qt.binding(() => {return containment.plasmoid.corona.editMode})
|
||||
}
|
||||
|
||||
// Those 2 elements have the same parameters as the overview effect
|
||||
MultiEffect {
|
||||
source: containment
|
||||
anchors.fill: parent
|
||||
blurEnabled: true
|
||||
blurMax: 64
|
||||
blur: 1.0
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
opacity: 0.7
|
||||
}
|
||||
|
||||
Item {
|
||||
id: editModeUi
|
||||
visible: editModeItem.open || xAnim.running
|
||||
x: Math.round(editModeItem.open ? editModeRect.x + editModeRect.width/2 - zoomedWidth/2 : 0)
|
||||
y: Math.round(editModeItem.open ? editModeRect.y + editModeRect.height/2 - zoomedHeight/2 + toolBar.height/2 : 0)
|
||||
width: editModeItem.open ? zoomedWidth : editModeItem.roundedRootWidth
|
||||
height: editModeItem.open ? zoomedHeight : editModeItem.roundedRootHeight
|
||||
property real zoomedWidth: Math.round(root.width * containmentParent.scaleFactor)
|
||||
property real zoomedHeight: Math.round(root.height * containmentParent.scaleFactor)
|
||||
|
||||
Kirigami.ShadowedRectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
width: Math.round(parent.width)
|
||||
height: Math.round(parent.height + toolBar.height + Kirigami.Units.largeSpacing)
|
||||
y: - toolBar.height - Kirigami.Units.largeSpacing
|
||||
|
||||
radius: editModeItem.open ? Kirigami.Units.cornerRadius : 0
|
||||
Behavior on radius {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
shadow {
|
||||
size: Kirigami.Units.gridUnit * 2
|
||||
color: Qt.rgba(0, 0, 0, 0.3)
|
||||
yOffset: 3
|
||||
}
|
||||
RowLayout {
|
||||
id: toolBar
|
||||
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
margins: Kirigami.Units.smallSpacing
|
||||
}
|
||||
Flow {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: implicitHeight
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
PC.ToolButton {
|
||||
id: addWidgetButton
|
||||
property QtObject qAction: containment?.plasmoid.internalAction("add widgets") || null
|
||||
text: qAction?.text
|
||||
icon.name: "view-group-symbolic"
|
||||
onClicked: qAction.trigger()
|
||||
}
|
||||
|
||||
PC.ToolButton {
|
||||
id: addPanelButton
|
||||
height: addWidgetButton.height
|
||||
property QtObject qAction: containment?.plasmoid.corona.action("add panel") || null
|
||||
text: qAction?.text
|
||||
icon.name: "list-add"
|
||||
Accessible.role: Accessible.ButtonMenu
|
||||
onClicked: containment.plasmoid.corona.showAddPanelContextMenu(mapToGlobal(0, height))
|
||||
}
|
||||
|
||||
PC.ToolButton {
|
||||
id: manageContainmentsButton
|
||||
property QtObject qAction: containment?.plasmoid.corona.action("manage-containments") || null
|
||||
text: qAction?.text
|
||||
visible: qAction?.visible || false
|
||||
icon.name: "configure-symbolic"
|
||||
onClicked: qAction.trigger()
|
||||
}
|
||||
}
|
||||
|
||||
PC.ToolButton {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
visible: Kirigami.Settings.hasTransientTouchInput || Kirigami.Settings.tabletMode
|
||||
|
||||
icon.name: "overflow-menu"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "More")
|
||||
|
||||
onClicked: {
|
||||
containment.openContextMenu(mapToGlobal(0, height));
|
||||
}
|
||||
}
|
||||
PC.ToolButton {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
icon.name: "dialog-ok-symbolic"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Exit Edit Mode")
|
||||
onClicked: containment.plasmoid.corona.editMode = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
id: xAnim
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: containment
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
layer.effect: Kirigami.ShadowedTexture {
|
||||
width: editModeItem.roundedRootWidth
|
||||
height: editModeItem.roundedRootHeight
|
||||
color: "transparent"
|
||||
|
||||
radius: editModeItem.open ? Kirigami.Units.cornerRadius : 0
|
||||
Behavior on radius {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
import QtQml
|
||||
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.ksvg as KSvg
|
||||
import org.kde.taskmanager as TaskManager
|
||||
import org.kde.kwindowsystem
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.shell.panel as Panel
|
||||
|
||||
import org.kde.plasma.plasmoid
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property Item containment
|
||||
|
||||
property bool floatingPrefix: floatingPanelSvg.usedPrefix === "floating"
|
||||
readonly property bool verticalPanel: containment?.plasmoid?.formFactor === PlasmaCore.Types.Vertical
|
||||
|
||||
readonly property real spacingAtMinSize: Math.floor(Math.max(1, panel.thickness - Kirigami.Units.iconSizes.smallMedium)/2)
|
||||
KSvg.FrameSvgItem {
|
||||
id: thickPanelSvg
|
||||
visible: false
|
||||
prefix: 'thick'
|
||||
imagePath: "widgets/panel-background"
|
||||
}
|
||||
KSvg.FrameSvgItem {
|
||||
id: floatingPanelSvg
|
||||
visible: false
|
||||
prefix: ['floating', '']
|
||||
imagePath: "widgets/panel-background"
|
||||
}
|
||||
|
||||
readonly property bool rightEdge: containment?.plasmoid?.location === PlasmaCore.Types.RightEdge
|
||||
readonly property bool bottomEdge: containment?.plasmoid?.location === PlasmaCore.Types.BottomEdge
|
||||
|
||||
readonly property int bottomFloatingPadding: Math.round(fixedBottomFloatingPadding * floatingness)
|
||||
readonly property int leftFloatingPadding: Math.round(fixedLeftFloatingPadding * floatingness)
|
||||
readonly property int rightFloatingPadding: Math.round(fixedRightFloatingPadding * floatingness)
|
||||
readonly property int topFloatingPadding: Math.round(fixedTopFloatingPadding * floatingness)
|
||||
|
||||
|
||||
// NOTE: Many of the properties in this file are accessed directly in C++ PanelView!
|
||||
// If you change these, make sure to also correct the related code in panelview.cpp.
|
||||
readonly property int fixedBottomFloatingPadding: floating && (floatingPrefix ? floatingPanelSvg.fixedMargins.bottom : 8)
|
||||
readonly property int fixedLeftFloatingPadding: floating && (floatingPrefix ? floatingPanelSvg.fixedMargins.left : 8)
|
||||
readonly property int fixedRightFloatingPadding: floating && (floatingPrefix ? floatingPanelSvg.fixedMargins.right : 8)
|
||||
readonly property int fixedTopFloatingPadding: floating && (floatingPrefix ? floatingPanelSvg.fixedMargins.top : 8)
|
||||
|
||||
readonly property int topPadding: Math.round(Math.min(thickPanelSvg.fixedMargins.top + Kirigami.Units.smallSpacing, spacingAtMinSize));
|
||||
readonly property int bottomPadding: Math.round(Math.min(thickPanelSvg.fixedMargins.bottom + Kirigami.Units.smallSpacing, spacingAtMinSize));
|
||||
readonly property int leftPadding: Math.round(Math.min(thickPanelSvg.fixedMargins.left + Kirigami.Units.smallSpacing, spacingAtMinSize));
|
||||
readonly property int rightPadding: Math.round(Math.min(thickPanelSvg.fixedMargins.right + Kirigami.Units.smallSpacing, spacingAtMinSize));
|
||||
|
||||
readonly property int minPanelHeight: translucentItem.minimumDrawingHeight
|
||||
readonly property int minPanelWidth: translucentItem.minimumDrawingWidth
|
||||
|
||||
// This value is read from panelview.cpp which needs it to decide which border should be enabled
|
||||
property real topShadowMargin: -floatingTranslucentItem.y
|
||||
property real leftShadowMargin: -floatingTranslucentItem.x
|
||||
property real rightShadowMargin: -(width - floatingTranslucentItem.width - floatingTranslucentItem.x)
|
||||
property real bottomShadowMargin: -(height - floatingTranslucentItem.height - floatingTranslucentItem.y)
|
||||
|
||||
property var panelMask: floatingness === 0 ? (panelOpacity === 1 ? opaqueItem.mask : translucentItem.mask) : (panelOpacity === 1 ? floatingOpaqueItem.mask : floatingTranslucentItem.mask)
|
||||
|
||||
// The point is read from panelview.cpp and is used as an offset for the mask
|
||||
readonly property point floatingTranslucentItemOffset: Qt.point(floatingTranslucentItem.x, floatingTranslucentItem.y)
|
||||
|
||||
TaskManager.VirtualDesktopInfo {
|
||||
id: virtualDesktopInfo
|
||||
}
|
||||
|
||||
TaskManager.ActivityInfo {
|
||||
id: activityInfo
|
||||
}
|
||||
|
||||
// We need to have a little gap between the raw visibleWindowsModel count
|
||||
// and actually determining if a window is touching.
|
||||
// This is because certain dialog windows start off with a position of (screenwidth/2, screenheight/2)
|
||||
// and they register as "touching" in the split-second before KWin can place them correctly.
|
||||
// This avoids the panel flashing if it is auto-hide etc and such a window is shown.
|
||||
// Examples of such windows: properties of a file on desktop, or portal "open with" dialog
|
||||
property bool touchingWindow: false
|
||||
property bool touchingWindowDirect: visibleWindowsModel.count > 0
|
||||
property bool showingDesktop: KWindowSystem.showingDesktop
|
||||
Timer {
|
||||
id: touchingWindowDebounceTimer
|
||||
interval: 10 // ms, I find that this value is enough while not causing unresponsiveness while dragging windows close
|
||||
onTriggered: root.touchingWindow = !KWindowSystem.showingDesktop && root.touchingWindowDirect
|
||||
}
|
||||
onTouchingWindowDirectChanged: touchingWindowDebounceTimer.start()
|
||||
onShowingDesktopChanged: touchingWindowDebounceTimer.start()
|
||||
|
||||
TaskManager.TasksModel {
|
||||
id: visibleWindowsModel
|
||||
filterByVirtualDesktop: true
|
||||
filterByActivity: true
|
||||
filterByScreen: false
|
||||
filterByRegion: TaskManager.RegionFilterMode.Intersect
|
||||
filterHidden: true
|
||||
filterMinimized: true
|
||||
|
||||
screenGeometry: panel.screenGeometry
|
||||
virtualDesktop: virtualDesktopInfo.currentDesktop
|
||||
activity: activityInfo.currentActivity
|
||||
|
||||
groupMode: TaskManager.TasksModel.GroupDisabled
|
||||
|
||||
Binding on regionGeometry {
|
||||
delayed: true
|
||||
value: panel.width, panel.height, panel.x, panel.y, panel.dogdeGeometryByDistance(panel.visibilityMode === Panel.Global.DodgeWindows ? -1 : 1) // +1 is for overlap detection, -1 is for snapping to panel
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.containment?.plasmoid ?? null
|
||||
function onActivated() {
|
||||
if (root.containment.plasmoid.status === PlasmaCore.Types.AcceptingInputStatus) {
|
||||
root.containment.plasmoid.status = PlasmaCore.Types.PassiveStatus;
|
||||
} else {
|
||||
root.containment.plasmoid.status = PlasmaCore.Types.AcceptingInputStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Floatingness is a value in [0, 1] that's multiplied to the floating margin; 0: not floating, 1: floating, between 0 and 1: animation between the two states
|
||||
readonly property int floatingnessAnimationDuration: Kirigami.Units.longDuration
|
||||
property double floatingnessTarget: 0.0 // The animation is handled in panelview.cpp for efficiency
|
||||
property double floatingness: 0.0
|
||||
|
||||
// PanelOpacity is a value in [0, 1] that's used as the opacity of the opaque elements over the transparent ones; values between 0 and 1 are used for animations
|
||||
property double panelOpacity
|
||||
Behavior on panelOpacity {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
KSvg.FrameSvgItem {
|
||||
id: translucentItem
|
||||
visible: root.floatingness === 0 && root.panelOpacity !== 1
|
||||
enabledBorders: panel.enabledBorders
|
||||
anchors.fill: floatingTranslucentItem
|
||||
imagePath: containment?.plasmoid?.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "widgets/panel-background"
|
||||
}
|
||||
KSvg.FrameSvgItem {
|
||||
id: floatingTranslucentItem
|
||||
visible: root.floatingness !== 0 && root.panelOpacity !== 1
|
||||
x: root.rightEdge ? root.fixedLeftFloatingPadding + root.fixedRightFloatingPadding * (1 - root.floatingness) : root.leftFloatingPadding
|
||||
y: root.bottomEdge ? root.fixedTopFloatingPadding + root.fixedBottomFloatingPadding * (1 - root.floatingness) : root.topFloatingPadding
|
||||
width: root.verticalPanel ? panel.thickness : parent.width - root.leftFloatingPadding - root.rightFloatingPadding
|
||||
height: root.verticalPanel ? parent.height - root.topFloatingPadding - root.bottomFloatingPadding : panel.thickness
|
||||
|
||||
imagePath: containment?.plasmoid?.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "widgets/panel-background"
|
||||
}
|
||||
KSvg.FrameSvgItem {
|
||||
id: floatingOpaqueItem
|
||||
visible: root.floatingness !== 0 && root.panelOpacity !== 0
|
||||
opacity: root.panelOpacity
|
||||
anchors.fill: floatingTranslucentItem
|
||||
imagePath: containment?.plasmoid?.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "solid/widgets/panel-background"
|
||||
}
|
||||
KSvg.FrameSvgItem {
|
||||
id: opaqueItem
|
||||
visible: root.panelOpacity !== 0 && root.floatingness === 0
|
||||
opacity: root.panelOpacity
|
||||
enabledBorders: panel.enabledBorders
|
||||
anchors.fill: floatingTranslucentItem
|
||||
imagePath: containment?.plasmoid?.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "solid/widgets/panel-background"
|
||||
}
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
root.parent.focus = false
|
||||
}
|
||||
|
||||
property bool isOpaque: panel.opacityMode === Panel.Global.Opaque
|
||||
property bool isTransparent: panel.opacityMode === Panel.Global.Translucent
|
||||
property bool isAdaptive: panel.opacityMode === Panel.Global.Adaptive
|
||||
property bool floating: panel.floating
|
||||
property bool hasCompositing: KWindowSystem.isPlatformX11 ? KX11Extras.compositingActive : true
|
||||
property var stateTriggers: [floating, touchingWindow, isOpaque, isAdaptive, isTransparent, hasCompositing, containment, panel.floatingApplets]
|
||||
onStateTriggersChanged: {
|
||||
let opaqueApplets = false
|
||||
let floatingApplets = false
|
||||
if ((!floating || touchingWindow) && (isOpaque || (touchingWindow && isAdaptive))) {
|
||||
panelOpacity = 1
|
||||
opaqueApplets = true
|
||||
floatingnessTarget = 0
|
||||
floatingApplets = (panel.floatingApplets && !floating)
|
||||
} else if ((!floating || touchingWindow) && (isTransparent || (!touchingWindow && isAdaptive))) {
|
||||
panelOpacity = 0
|
||||
floatingnessTarget = 0
|
||||
floatingApplets = (panel.floatingApplets && !floating)
|
||||
} else if ((floating && !touchingWindow) && (isTransparent || isAdaptive)) {
|
||||
panelOpacity = 0
|
||||
floatingnessTarget = 1
|
||||
floatingApplets = true
|
||||
} else if (floating && !touchingWindow && isOpaque) {
|
||||
panelOpacity = 1
|
||||
opaqueApplets = true
|
||||
floatingnessTarget = 1
|
||||
floatingApplets = true
|
||||
}
|
||||
|
||||
// Exceptions: panels with not NormalPanel visibilityMode
|
||||
// should never de-float, and we should not have transparent
|
||||
// panels when on X11 with compositing not active.
|
||||
if (panel.visibilityMode != Panel.Global.NormalPanel && floating) {
|
||||
floatingnessTarget = 1
|
||||
floatingApplets = true
|
||||
}
|
||||
if (!KWindowSystem.isPlatformWayland && !KX11Extras.compositingActive) {
|
||||
opaqueApplets = false
|
||||
panelOpacity = 0
|
||||
}
|
||||
|
||||
// Not using panelOpacity to check as it has a NumberAnimation, and it will thus
|
||||
// be still read as the initial value here, before the animation starts.
|
||||
if (containment) {
|
||||
if (opaqueApplets) {
|
||||
containment.plasmoid.containmentDisplayHints |= PlasmaCore.Types.ContainmentPrefersOpaqueBackground
|
||||
} else {
|
||||
containment.plasmoid.containmentDisplayHints &= ~PlasmaCore.Types.ContainmentPrefersOpaqueBackground
|
||||
}
|
||||
if (floatingApplets) {
|
||||
containment.plasmoid.containmentDisplayHints |= PlasmaCore.Types.ContainmentPrefersFloatingApplets
|
||||
} else {
|
||||
containment.plasmoid.containmentDisplayHints &= ~PlasmaCore.Types.ContainmentPrefersFloatingApplets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function adjustPrefix() {
|
||||
if (!containment) {
|
||||
return "";
|
||||
}
|
||||
var pre;
|
||||
switch (containment.plasmoid.location) {
|
||||
case PlasmaCore.Types.LeftEdge:
|
||||
pre = "west";
|
||||
break;
|
||||
case PlasmaCore.Types.TopEdge:
|
||||
pre = "north";
|
||||
break;
|
||||
case PlasmaCore.Types.RightEdge:
|
||||
pre = "east";
|
||||
break;
|
||||
case PlasmaCore.Types.BottomEdge:
|
||||
pre = "south";
|
||||
break;
|
||||
default:
|
||||
pre = "";
|
||||
break;
|
||||
}
|
||||
translucentItem.prefix = opaqueItem.prefix = floatingTranslucentItem.prefix = floatingOpaqueItem.prefix = [pre, ""];
|
||||
}
|
||||
|
||||
onContainmentChanged: {
|
||||
if (!containment) {
|
||||
return;
|
||||
}
|
||||
containment.parent = containmentParent;
|
||||
containment.visible = true;
|
||||
containment.anchors.fill = containmentParent;
|
||||
containment.plasmoid.locationChanged.connect(adjustPrefix);
|
||||
adjustPrefix();
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: panel
|
||||
property: "length"
|
||||
when: containment
|
||||
value: {
|
||||
if (!containment) {
|
||||
return;
|
||||
}
|
||||
if (root.verticalPanel) {
|
||||
if (containment.Layout.fillHeight) {
|
||||
if (panel.lengthMode == Panel.Global.Custom) {
|
||||
return panel.maximumHeight
|
||||
} else {
|
||||
return panel.screenGeometry.height
|
||||
}
|
||||
}
|
||||
return containment.Layout.preferredHeight
|
||||
} else {
|
||||
if (containment.Layout.fillWidth) {
|
||||
if (panel.lengthMode == Panel.Global.Custom) {
|
||||
return panel.maximumWidth
|
||||
} else {
|
||||
return panel.screenGeometry.width
|
||||
}
|
||||
}
|
||||
return containment.Layout.preferredWidth
|
||||
}
|
||||
}
|
||||
restoreMode: Binding.RestoreBinding
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: panel
|
||||
property: "backgroundHints"
|
||||
when: containment
|
||||
value: {
|
||||
if (!containment) {
|
||||
return;
|
||||
}
|
||||
|
||||
return containment.plasmoid.backgroundHints;
|
||||
}
|
||||
restoreMode: Binding.RestoreBinding
|
||||
}
|
||||
|
||||
KSvg.FrameSvgItem {
|
||||
|
||||
Accessible.name: i18nc("@info:whatsthis Accessible name", "Panel Focus Indicator")
|
||||
|
||||
x: root.verticalPanel || !panel.activeFocusItem
|
||||
? translucentItem.x
|
||||
: Math.max(panel.activeFocusItem.Kirigami.ScenePosition.x, panel.activeFocusItem.Kirigami.ScenePosition.x)
|
||||
y: root.verticalPanel && panel.activeFocusItem
|
||||
? Math.max(panel.activeFocusItem.Kirigami.ScenePosition.y, panel.activeFocusItem.Kirigami.ScenePosition.y)
|
||||
: translucentItem.y
|
||||
|
||||
width: panel.activeFocusItem
|
||||
? (root.verticalPanel ? translucentItem.width : Math.min(panel.activeFocusItem.width, panel.activeFocusItem.width))
|
||||
: 0
|
||||
height: panel.activeFocusItem
|
||||
? (root.verticalPanel ? Math.min(panel.activeFocusItem.height, panel.activeFocusItem.height) : translucentItem.height)
|
||||
: 0
|
||||
|
||||
visible: panel.active && panel.activeFocusItem
|
||||
|
||||
imagePath: "widgets/tabbar"
|
||||
prefix: {
|
||||
if (!root.containment) {
|
||||
return "";
|
||||
}
|
||||
var prefix = ""
|
||||
switch (root.containment.plasmoid.location) {
|
||||
case PlasmaCore.Types.LeftEdge:
|
||||
prefix = "west-active-tab";
|
||||
break;
|
||||
case PlasmaCore.Types.TopEdge:
|
||||
prefix = "north-active-tab";
|
||||
break;
|
||||
case PlasmaCore.Types.RightEdge:
|
||||
prefix = "east-active-tab";
|
||||
break;
|
||||
default:
|
||||
prefix = "south-active-tab";
|
||||
}
|
||||
if (!hasElementPrefix(prefix)) {
|
||||
prefix = "active-tab";
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
Item {
|
||||
id: containmentParent
|
||||
anchors.centerIn: root.isOpaque ? floatingOpaqueItem : floatingTranslucentItem
|
||||
width: root.verticalPanel ? panel.thickness : root.width - root.fixedLeftFloatingPadding - root.fixedRightFloatingPadding
|
||||
height: root.verticalPanel ? root.height - root.fixedBottomFloatingPadding - root.fixedTopFloatingPadding : panel.thickness
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Item {
|
||||
// Using childrenRect.width causes a binding loop since we can only get the
|
||||
// actual width, not the implicitWidth--which is what we would want
|
||||
width: Math.max(title.implicitWidth, subtitle.implicitWidth)
|
||||
height: childrenRect.height
|
||||
|
||||
HoverHandler {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onTapped: desktop.showPreviewBannerMenu(mapToGlobal(point.position))
|
||||
}
|
||||
|
||||
PlasmaExtras.ShadowedLabel {
|
||||
id: title
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
}
|
||||
z: 2
|
||||
text: desktop.previewBannerTitle
|
||||
// Emulate the size of a level 1 heading
|
||||
font.pointSize: Math.round(Kirigami.Theme.defaultFont.pointSize * 1.35)
|
||||
}
|
||||
|
||||
PlasmaExtras.ShadowedLabel {
|
||||
id: subtitle
|
||||
anchors {
|
||||
top: title.bottom
|
||||
right: parent.right
|
||||
}
|
||||
z: 2
|
||||
text: desktop.previewBannerText
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user