Build the Panama Control Center
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
import QtQuick
|
||||
import QtQuick.Dialogs
|
||||
import qs.config
|
||||
import qs.services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool expanded: false
|
||||
signal toggleExpanded
|
||||
|
||||
readonly property var phone: KdeConnect.preferredPhone
|
||||
readonly property var actionModels: [
|
||||
{ id: "share", glyph: "\u{F0142}", label: "Send file" },
|
||||
{ id: "clipboard", glyph: "\u{F014C}", label: "Clipboard" },
|
||||
{ id: "ring", glyph: "\u{F009A}", label: "Ring" }
|
||||
].filter(item => KdeConnect.supports(item.id))
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
Column {
|
||||
id: content
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
|
||||
ControlSectionHeader {
|
||||
width: parent.width
|
||||
label: "Phone"
|
||||
action: root.phone ? (root.expanded ? "Details ⌃" : "Details ›") : "Refresh"
|
||||
onActionTriggered: {
|
||||
if (root.phone)
|
||||
root.toggleExpanded();
|
||||
else
|
||||
KdeConnect.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: phoneContent.implicitHeight + 22
|
||||
radius: Theme.cardRadius + 2
|
||||
color: Theme.alpha(Theme.fg, 0.055)
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.fg, 0.05)
|
||||
|
||||
Column {
|
||||
id: phoneContent
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: 11
|
||||
spacing: 9
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 46
|
||||
|
||||
Rectangle {
|
||||
id: phoneSilhouette
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 29
|
||||
height: 43
|
||||
radius: 8
|
||||
color: Theme.alpha(Theme.accent, 0.07)
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.fg, 0.42)
|
||||
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 4
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 10
|
||||
height: 2
|
||||
radius: 1
|
||||
color: Theme.alpha(Theme.fg, 0.35)
|
||||
}
|
||||
Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: -3
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
visible: KdeConnect.phoneReachable
|
||||
color: Theme.ok
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.left: phoneSilhouette.right
|
||||
anchors.leftMargin: 11
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 3
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: root.phone?.name ?? (KdeConnect.available ? "No paired phone" : "KDE Connect unavailable")
|
||||
color: Theme.fg
|
||||
elide: Text.ElideRight
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
Text {
|
||||
width: parent.width
|
||||
text: root.phone
|
||||
? (KdeConnect.phoneReachable ? "● Nearby on Wi-Fi" : "Not nearby")
|
||||
: (KdeConnect.available ? "Pair a device to continue" : "Open settings to repair the service")
|
||||
color: KdeConnect.phoneReachable ? Theme.ok : Theme.fgDim
|
||||
elide: Text.ElideRight
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: actionsGrid
|
||||
width: parent.width
|
||||
columns: Math.max(1, root.actionModels.length)
|
||||
spacing: 7
|
||||
visible: root.actionModels.length > 0
|
||||
|
||||
Repeater {
|
||||
model: root.actionModels
|
||||
|
||||
Rectangle {
|
||||
id: actionButton
|
||||
required property var modelData
|
||||
width: (actionsGrid.width - actionsGrid.spacing * Math.max(0, root.actionModels.length - 1)) / Math.max(1, root.actionModels.length)
|
||||
height: 48
|
||||
radius: 11
|
||||
color: actionMouse.containsMouse && actionMouse.enabled
|
||||
? Theme.alpha(Theme.accent, 0.15)
|
||||
: Theme.alpha(Theme.accent, 0.075)
|
||||
opacity: actionMouse.enabled ? 1 : 0.48
|
||||
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 7
|
||||
text: actionButton.modelData.glyph
|
||||
color: Theme.accent
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 13
|
||||
}
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 6
|
||||
text: actionButton.modelData.label
|
||||
color: Theme.fg
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 9
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
MouseArea {
|
||||
id: actionMouse
|
||||
anchors.fill: parent
|
||||
enabled: KdeConnect.phoneReachable && !KdeConnect.transferActive
|
||||
hoverEnabled: true
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: root.invoke(actionButton.modelData.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
visible: root.phone && !KdeConnect.phoneReachable && root.actionModels.length > 0
|
||||
text: "Actions become available when the iPhone reconnects"
|
||||
color: Theme.fgMuted
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
visible: KdeConnect.lastError !== ""
|
||||
text: root.errorText(KdeConnect.lastError)
|
||||
color: Theme.warn
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
visible: KdeConnect.recentExchange !== null
|
||||
color: Theme.alpha(Theme.fg, 0.07)
|
||||
}
|
||||
|
||||
RecentExchange {
|
||||
width: parent.width
|
||||
height: visible ? implicitHeight : 0
|
||||
visible: KdeConnect.recentExchange !== null
|
||||
exchange: KdeConnect.recentExchange ?? ({})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
width: parent.width
|
||||
expanded: root.expanded && root.phone !== null
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
|
||||
RowButton {
|
||||
width: parent.width
|
||||
icon: "network-wireless-symbolic"
|
||||
label: "Connection"
|
||||
sublabel: KdeConnect.phoneReachable ? "Nearby on Wi-Fi" : "Waiting for the phone"
|
||||
selected: KdeConnect.phoneReachable
|
||||
onClicked: KdeConnect.refresh()
|
||||
}
|
||||
RowButton {
|
||||
width: parent.width
|
||||
icon: "preferences-system-symbolic"
|
||||
label: "Device settings"
|
||||
sublabel: "Pairing, plugins and service health"
|
||||
onClicked: {
|
||||
ShellState.close();
|
||||
ShellState.openSettings("connectivity");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
title: root.phone ? "Send a file to " + root.phone.name : "Send a file"
|
||||
fileMode: FileDialog.OpenFile
|
||||
nameFilters: ["All files (*)"]
|
||||
onAccepted: {
|
||||
const path = root.localPath(selectedFile);
|
||||
if (path !== "")
|
||||
KdeConnect.sendFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
function invoke(action: string): void {
|
||||
if (action === "share")
|
||||
fileDialog.open();
|
||||
else if (action === "clipboard")
|
||||
KdeConnect.sendClipboard();
|
||||
else if (action === "ring")
|
||||
KdeConnect.ring();
|
||||
}
|
||||
|
||||
function localPath(selectedUrl: url): string {
|
||||
const value = String(selectedUrl);
|
||||
if (!value.startsWith("file://"))
|
||||
return "";
|
||||
return decodeURIComponent(value.slice(7));
|
||||
}
|
||||
|
||||
function errorText(code: string): string {
|
||||
if (code === "device-offline")
|
||||
return "The iPhone is not nearby";
|
||||
if (code === "unsupported-action")
|
||||
return "That action is not available on this iPhone";
|
||||
return "The phone action did not finish";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user