Files
home/.local/share/plasma/plasmoids/KdeControlStation/contents/ui/layouts/Custom.qml
2025-10-08 10:35:48 +02:00

245 lines
7.3 KiB
QML

import QtQml 2.15
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.0
import org.kde.plasma.components as PlasmaComponents
import "../components" as Components
import "components/Custom" as LayoutComponents
import org.kde.plasma.plasmoid
Item {
id: customLayout
anchors.fill: parent
implicitHeight: mainGrid.implicitHeight + editLayoutBtn.height
property var customLayoutModel: Plasmoid.configuration.customLayoutModel
property var widgetsModel: masterWidgetsModel.widgetsModel
property real totalWidgetsHeight: 0
Component.onCompleted: {
/* If user has used 'Custom' layout it retrieves last configration
from plasmoid config and sets it to masterWidgetsModel in order to
be able to manipulate the data */
if(customLayoutModel !== "null"){
var datamodel = JSON.parse(customLayoutModel);
masterWidgetsModel.widgetsModel = datamodel;
}
}
LayoutComponents.Model {
id: masterWidgetsModel
defaultCellHeight: mainGrid.cellHeight
}
readonly property var initialDraggedItemCoords: []
Connections {
target: masterWidgetsModel
function onModelUpdated () {
customLayout.totalWidgetsHeight = 0;
repeater.model = widgetsModel;
}
}
Connections {
target: root
function onEditingLayoutChanged() {
repeater.model = null;
repeater.model = widgetsModel;
}
}
GridLayout {
id: mainGrid
anchors.fill: parent
anchors.bottomMargin: editLayoutBtn.height
columns: 4
rows: customLayout.totalWidgetsHeight / (cellWidth*4)
columnSpacing: root.editingLayout ? 5 : 0
rowSpacing: root.editingLayout ? 5 : 0
property var cellWidth: root.editingLayout ? ((root.fullRepWidth-columnSpacing*columns) / columns) : root.fullRepWidth / columns
property var cellHeight: (root.sectionHeight*1.3) / 3
Repeater {
id: repeater
model: widgetsModel
delegate: Loader {
id: loader
required property var model
required property var index
property int itemIndex: index
property var name: model.name
property var modelProps: model.props
asynchronous: true
source: model.componentUrl
width: model.width ? model.width : mainGrid.cellWidth * model.colSpan
height: model.height ? model.height : mainGrid.cellHeight
Layout.columnSpan: model.colSpan
Layout.rowSpan: model.rowSpan ? model.rowSpan : 1
z: Drag.active ? 4 : 1
Drag.active: mouseArea.drag.active // Activate drag when mouse is pressed
Drag.source: loader // Reference to the item being dragged
onLoaded: {
// Sets properties from model to loaded item
if (loader.item && loader.modelProps) {
for (const prop in loader.modelProps){
loader.item[prop] = loader.modelProps[prop];
}
}
// Recalculate totalWidgetsHeight to calculate number of rows
customLayout.totalWidgetsHeight += loader.width
}
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: loader
enabled: root.editingLayout
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.DragMoveCursor//Qt.PointingHandCursor
onClicked: mouse => {
if ((mouse.button == Qt.RightButton) && model.actions ) {
contextMenu.popup(mouse.x, mouse.y);
}
}
onPressed: {
customLayout.initialDraggedItemCoords[0] = loader.x;
customLayout.initialDraggedItemCoords[1] = loader.y;
}
onReleased: {
// Ensure the drag operation is completed by calling drop()
loader.Drag.drop();
}
}
LayoutComponents.ActionMenu {
id: contextMenu
model: loader.model.actions
}
Button {
id: removeItemButton
anchors.top: parent.top
anchors.right: parent.right
visible: root.editingLayout
Text {
text: "x"
anchors.centerIn: parent
color: "#ffffff"
font.pointSize: 11
}
z: 10
width: 20
height: 20
background: Rectangle {
color: root.redColor
radius: height / 2
}
onClicked: {
masterWidgetsModel.remove(itemIndex);
}
}
}
}
}
LayoutComponents.Footer {
id: editLayoutBtn
height: 40
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
}
DropArea {
id: dropArea
anchors.fill: parent // or define specific anchors
onDropped: drag => {
var above = mainGrid.childAt(drag.x, drag.y);
if (above && above !== drag.source) {
masterWidgetsModel.move(drag.source.itemIndex, above.itemIndex);
} else {
let item = drag.source;
if(isFreeSpace(item.x, item.y, item)){
let prevItem = findClosestItem(item.x, item.y, item);
if(prevItem){
masterWidgetsModel.insert(item.itemIndex, prevItem.itemIndex+1);
}
}
drag.source.x = customLayout.initialDraggedItemCoords[0];
drag.source.y = customLayout.initialDraggedItemCoords[1];
}
}
function findClosestItem(initXPoint, initYpoint, item){
let foundedItem = null
let itemYcenter = initYpoint + item.height/2
let itemWidth = item.width
for (let i = initXPoint-10; i > 0; i-=itemWidth ) {
foundedItem = mainGrid.childAt(i, itemYcenter);
if(foundedItem) {
return foundedItem;
break;
}
}
return foundedItem;
}
function isFreeSpace(initXPoint, initYpoint, item) {
let finalXpoint = initXPoint + item.width;
let isFree = true;
let foundedItem = mainGrid.childAt(finalXpoint, initYpoint);
if((finalXpoint > mainGrid.width) || foundedItem) {
isFree = false;
return isFree;
}
return isFree;
}
}
}