mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
added fly in outgoing transfer card and compact mode
This commit is contained in:
@@ -10,10 +10,11 @@ RowLayout {
|
||||
spacing: 0
|
||||
|
||||
property string pendingPath: ""
|
||||
property bool pendingTransfer: false
|
||||
property bool incomingShare: false
|
||||
property int currentIndex: 0
|
||||
property var selectedTransferId: 0
|
||||
property var activeOutgoingTransferId: 0
|
||||
property bool outgoingStartFailed: false
|
||||
readonly property bool outgoingTransferActive: activeOutgoingTransferId != 0
|
||||
|
||||
Connections {
|
||||
target: backend
|
||||
@@ -24,6 +25,12 @@ RowLayout {
|
||||
selectedTransferId = share_target_id;
|
||||
currentIndex = 2;
|
||||
}
|
||||
|
||||
function onOutgoingTransferStartFailed(share_target_id) {
|
||||
if (share_target_id == topLayout.activeOutgoingTransferId) {
|
||||
topLayout.outgoingStartFailed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -35,12 +42,14 @@ RowLayout {
|
||||
}
|
||||
|
||||
function onShareTargetSelected(shareTargetId) {
|
||||
if (topLayout.pendingPath.length === 0) {
|
||||
if (topLayout.pendingPath.length === 0
|
||||
|| topLayout.outgoingTransferActive) {
|
||||
return;
|
||||
}
|
||||
topLayout.selectedTransferId = shareTargetId;
|
||||
backend.sendFile(shareTargetId, topLayout.pendingPath);
|
||||
topLayout.currentIndex = 2;
|
||||
if (backend.sendFile(shareTargetId, topLayout.pendingPath)) {
|
||||
topLayout.outgoingStartFailed = false;
|
||||
topLayout.activeOutgoingTransferId = shareTargetId;
|
||||
}
|
||||
}
|
||||
|
||||
function onCancelPendingShareRequested() {
|
||||
@@ -49,8 +58,25 @@ RowLayout {
|
||||
}
|
||||
|
||||
function cancelPendingShare() {
|
||||
if (outgoingTransferActive) {
|
||||
return;
|
||||
}
|
||||
returnHome();
|
||||
}
|
||||
|
||||
function finishOutgoingTransfer(shareTargetId) {
|
||||
if (shareTargetId != activeOutgoingTransferId) {
|
||||
return;
|
||||
}
|
||||
activeOutgoingTransferId = 0;
|
||||
outgoingStartFailed = false;
|
||||
}
|
||||
|
||||
function returnHome() {
|
||||
pendingPath = "";
|
||||
selectedTransferId = 0;
|
||||
activeOutgoingTransferId = 0;
|
||||
outgoingStartFailed = false;
|
||||
currentIndex = 0;
|
||||
backend.startReceive();
|
||||
}
|
||||
@@ -62,62 +88,178 @@ RowLayout {
|
||||
|
||||
Sidebar {
|
||||
pendingPath: topLayout.pendingPath
|
||||
cancelEnabled: !topLayout.outgoingTransferActive
|
||||
}
|
||||
StackLayout {
|
||||
id: contentStack
|
||||
|
||||
Item {
|
||||
id: contentPane
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
currentIndex: topLayout.currentIndex
|
||||
|
||||
Drop {}
|
||||
StackLayout {
|
||||
id: contentStack
|
||||
anchors.fill: parent
|
||||
currentIndex: topLayout.currentIndex
|
||||
|
||||
SearchingDevices {}
|
||||
Drop {}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
SearchingDevices {
|
||||
transferLocked: topLayout.outgoingTransferActive
|
||||
activeTransferId: topLayout.activeOutgoingTransferId
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: transferRepeater
|
||||
model: backend.transfers
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
IncomingShare {
|
||||
anchors.fill: parent
|
||||
Repeater {
|
||||
id: transferRepeater
|
||||
model: backend.transfers
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("transferId: " + model.transferId)
|
||||
console.log("selectedTransferId: " + topLayout.selectedTransferId)
|
||||
console.log(model.transferId == topLayout.selectedTransferId)
|
||||
IncomingShare {
|
||||
anchors.fill: parent
|
||||
|
||||
visible: model.direction === "receive"
|
||||
&& model.transferId == topLayout.selectedTransferId
|
||||
shareTargetId: model.transferId
|
||||
direction: model.direction
|
||||
filename: model.localPath
|
||||
targetname: model.deviceName
|
||||
progressValue: model.progress
|
||||
status: model.status
|
||||
totalBytes: model.totalBytes
|
||||
transferredBytes: model.transferredBytes
|
||||
totalAttachmentsCount: model.totalAttachmentsCount
|
||||
transferredAttachmentsCount: model.transferredAttachmentsCount
|
||||
isFinalStatus: model.isFinalStatus
|
||||
awaitingLocalConfirmation: model.awaitingLocalConfirmation
|
||||
onReturnHomeRequested: topLayout.returnHome()
|
||||
}
|
||||
}
|
||||
|
||||
visible: topLayout.selectedTransferId == 0 || model.transferId == topLayout.selectedTransferId
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#DCF5FF"
|
||||
visible: transferRepeater.count === 0
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Preparing transfer..."
|
||||
color: "#377B95"
|
||||
font.pointSize: 18
|
||||
font.weight: 600
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: backend.transfers
|
||||
|
||||
Item {
|
||||
id: outgoingNotification
|
||||
|
||||
readonly property bool terminal: model.isFinalStatus
|
||||
|| topLayout.outgoingStartFailed
|
||||
property bool cancelPending: false
|
||||
property bool exiting: false
|
||||
|
||||
width: Math.min(contentPane.width * 0.9, 700)
|
||||
height: 156
|
||||
x: (contentPane.width - width) / 2
|
||||
y: -height - 8
|
||||
z: 100
|
||||
visible: topLayout.currentIndex === 1
|
||||
&& model.direction === "send"
|
||||
&& model.transferId == topLayout.activeOutgoingTransferId
|
||||
|
||||
function showCard() {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
exiting = false;
|
||||
cancelPending = false;
|
||||
finalStatusTimer.stop();
|
||||
exitAnimation.stop();
|
||||
y = -height - 8;
|
||||
enterAnimation.restart();
|
||||
if (terminal) {
|
||||
finalStatusTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
Qt.callLater(showCard);
|
||||
} else {
|
||||
finalStatusTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
onTerminalChanged: {
|
||||
if (visible && terminal) {
|
||||
finalStatusTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (visible) {
|
||||
Qt.callLater(showCard);
|
||||
}
|
||||
}
|
||||
|
||||
TransferCard {
|
||||
anchors.fill: parent
|
||||
compact: true
|
||||
shareTargetId: model.transferId
|
||||
direction: model.direction
|
||||
filename: model.localPath
|
||||
targetname: model.deviceName
|
||||
progressValue: model.progress
|
||||
status: model.status
|
||||
status: topLayout.outgoingStartFailed ? "kStartFailed" : model.status
|
||||
isFinalStatus: outgoingNotification.terminal
|
||||
cancelEnabled: !outgoingNotification.cancelPending
|
||||
totalBytes: model.totalBytes
|
||||
transferredBytes: model.transferredBytes
|
||||
totalAttachmentsCount: model.totalAttachmentsCount
|
||||
transferredAttachmentsCount: model.transferredAttachmentsCount
|
||||
isFinalStatus: model.isFinalStatus
|
||||
awaitingLocalConfirmation: model.awaitingLocalConfirmation
|
||||
onCancelRequested: {
|
||||
outgoingNotification.cancelPending = true;
|
||||
backend.cancel(model.transferId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#DCF5FF"
|
||||
visible: transferRepeater.count === 0
|
||||
NumberAnimation {
|
||||
id: enterAnimation
|
||||
target: outgoingNotification
|
||||
property: "y"
|
||||
to: 16
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Preparing transfer..."
|
||||
color: "#377B95"
|
||||
font.pointSize: 18
|
||||
font.weight: 600
|
||||
Timer {
|
||||
id: finalStatusTimer
|
||||
interval: 2000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
outgoingNotification.exiting = true;
|
||||
exitAnimation.restart();
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: exitAnimation
|
||||
target: outgoingNotification
|
||||
property: "y"
|
||||
to: -outgoingNotification.height - 8
|
||||
duration: 250
|
||||
easing.type: Easing.InCubic
|
||||
onStopped: {
|
||||
if (outgoingNotification.exiting) {
|
||||
outgoingNotification.exiting = false;
|
||||
topLayout.finishOutgoingTransfer(model.transferId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ qt_resource_via_qrc(
|
||||
"SearchingDevices.qml",
|
||||
"Targets.qml",
|
||||
"IncomingShare.qml",
|
||||
"TransferCard.qml",
|
||||
"googlesans_var.ttf",
|
||||
"icons/file.svg",
|
||||
"icons/laptop.svg",
|
||||
|
||||
@@ -23,6 +23,8 @@ Rectangle {
|
||||
property var totalBytes: 0
|
||||
property var transferredBytes: 0
|
||||
|
||||
signal returnHomeRequested()
|
||||
|
||||
function baseName(path) {
|
||||
if (!path || path.length === 0) {
|
||||
return direction === "send" ? "Selected file" : transferSummary()
|
||||
@@ -91,140 +93,24 @@ Rectangle {
|
||||
return direction === "send" ? "Starting send" : "Preparing transfer"
|
||||
}
|
||||
|
||||
Item {
|
||||
TransferCard {
|
||||
visible: root.transferring
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.width * 0.9, 700)
|
||||
height: 212
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 24
|
||||
color: "#FFFFFF"
|
||||
border.color: "#BCE5F5"
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 28
|
||||
spacing: 24
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 96
|
||||
Layout.preferredHeight: 96
|
||||
radius: 24
|
||||
color: "#E8F7FC"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 60
|
||||
height: 60
|
||||
source: "qrc:/icons/file.svg"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
smooth: true
|
||||
antialiasing: true
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: root.targetname
|
||||
font.pointSize: 17
|
||||
font.weight: 700
|
||||
color: "#1A1C1E"
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.baseName(root.filename)
|
||||
color: "#57707A"
|
||||
font.pointSize: 12
|
||||
elide: Text.ElideMiddle
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
id: transferProgress
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 1
|
||||
value: Math.max(0, Math.min(1, root.progressValue))
|
||||
|
||||
background: Rectangle {
|
||||
implicitHeight: 10
|
||||
radius: 5
|
||||
color: "#DDEEF5"
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
Rectangle {
|
||||
width: transferProgress.visualPosition * parent.width
|
||||
height: 10
|
||||
radius: 5
|
||||
color: "#0D6D90"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: root.statusText()
|
||||
color: "#57707A"
|
||||
font.pointSize: 12
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
text: Math.round(Math.max(0, Math.min(1, root.progressValue)) * 100) + "%"
|
||||
color: "#0D6D90"
|
||||
font.pointSize: 13
|
||||
font.weight: 700
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
id: cancelButton
|
||||
text: "Cancel"
|
||||
visible: !root.isFinalStatus
|
||||
onClicked: backend.cancel(root.shareTargetId)
|
||||
|
||||
contentItem: Text {
|
||||
text: cancelButton.text
|
||||
font.pointSize: 14
|
||||
font.weight: 600
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 118
|
||||
implicitHeight: 46
|
||||
radius: 12
|
||||
color: cancelButton.hovered ? "#B62828" : "#D94C4C"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shareTargetId: root.shareTargetId
|
||||
direction: root.direction
|
||||
filename: root.filename
|
||||
targetname: root.targetname
|
||||
progressValue: root.progressValue
|
||||
status: root.status
|
||||
isFinalStatus: root.isFinalStatus
|
||||
totalBytes: root.totalBytes
|
||||
totalAttachmentsCount: root.totalAttachmentsCount
|
||||
transferredAttachmentsCount: root.transferredAttachmentsCount
|
||||
finalActionText: root.isFinalStatus ? "Go back home" : ""
|
||||
onCancelRequested: backend.cancel(root.shareTargetId)
|
||||
onFinalActionRequested: root.returnHomeRequested()
|
||||
}
|
||||
|
||||
Item {
|
||||
|
||||
@@ -3,10 +3,14 @@ import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
color: "transparent"
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
property bool transferLocked: false
|
||||
property var activeTransferId: 0
|
||||
|
||||
Rectangle {
|
||||
id: pulseButton
|
||||
property real pulseSize: Math.min(parent.width, parent.height) * 0.5
|
||||
@@ -83,6 +87,8 @@ Rectangle {
|
||||
|
||||
Targets {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
interactionLocked: root.transferLocked
|
||||
activeTransferId: root.activeTransferId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ Rectangle {
|
||||
property string deviceName: "Unknown Device"
|
||||
property string iconSource: "qrc:icons/smartphone.svg"
|
||||
property var shareTargetId: 0
|
||||
property bool hovered: targetMouseArea.containsMouse
|
||||
property bool interactionEnabled: true
|
||||
property bool hovered: interactionEnabled && targetMouseArea.containsMouse
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
@@ -47,6 +48,7 @@ Rectangle {
|
||||
|
||||
Button {
|
||||
id: iconButton
|
||||
enabled: rootItem.interactionEnabled
|
||||
icon.source: rootItem.iconSource
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
@@ -63,8 +65,9 @@ Rectangle {
|
||||
MouseArea {
|
||||
id: targetMouseArea
|
||||
anchors.fill: parent
|
||||
enabled: rootItem.interactionEnabled
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
cursorShape: rootItem.interactionEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: {
|
||||
EventBus.shareTargetSelected(rootItem.shareTargetId);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ Rectangle {
|
||||
Layout.preferredWidth: 300
|
||||
|
||||
property string pendingPath: ""
|
||||
property bool cancelEnabled: true
|
||||
|
||||
color: "#CBF0FF"
|
||||
|
||||
@@ -109,6 +110,8 @@ Rectangle {
|
||||
id: cancelbutton
|
||||
Layout.fillWidth: true
|
||||
text: "Cancel"
|
||||
enabled: sidebar.cancelEnabled
|
||||
opacity: enabled ? 1 : 0.55
|
||||
onClicked: EventBus.cancelPendingShareRequested()
|
||||
contentItem: Text {
|
||||
text: cancelbutton.text
|
||||
|
||||
@@ -7,6 +7,8 @@ Row {
|
||||
spacing: 10
|
||||
|
||||
property bool useDummyTargets: false
|
||||
property bool interactionLocked: false
|
||||
property var activeTransferId: 0
|
||||
|
||||
ListModel {
|
||||
id: dummyTargets
|
||||
@@ -45,6 +47,15 @@ Row {
|
||||
width: 100
|
||||
height: 130
|
||||
color: "transparent"
|
||||
opacity: !shareTargetsRow.interactionLocked
|
||||
|| model.targetId == shareTargetsRow.activeTransferId ? 1 : 0.32
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 180
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
@@ -55,6 +66,7 @@ Row {
|
||||
ShareTarget {
|
||||
shareTargetId: model.targetId
|
||||
deviceName: model.deviceName
|
||||
interactionEnabled: !shareTargetsRow.interactionLocked
|
||||
iconSource: {
|
||||
if (model.type === 2)
|
||||
return "qrc:icons/laptop.svg"
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: 700
|
||||
implicitHeight: compact ? 156 : 212
|
||||
|
||||
property bool compact: false
|
||||
property var shareTargetId: 0
|
||||
property string direction: "send"
|
||||
property string filename: ""
|
||||
property string targetname: "Unknown device"
|
||||
property real progressValue: 0
|
||||
property string status: "kUnknown"
|
||||
property bool isFinalStatus: false
|
||||
property bool cancelEnabled: true
|
||||
property int totalAttachmentsCount: 0
|
||||
property int transferredAttachmentsCount: 0
|
||||
property var totalBytes: 0
|
||||
property string finalActionText: ""
|
||||
|
||||
signal cancelRequested()
|
||||
signal finalActionRequested()
|
||||
|
||||
function baseName(path) {
|
||||
if (!path || path.length === 0) {
|
||||
return direction === "send" ? "Selected file" : transferSummary()
|
||||
}
|
||||
const normalized = decodeURIComponent(path).replace("file://", "")
|
||||
const parts = normalized.split("/")
|
||||
return parts.length === 0 ? normalized : parts[parts.length - 1]
|
||||
}
|
||||
|
||||
function transferSummary() {
|
||||
if (totalAttachmentsCount > 0) {
|
||||
return totalAttachmentsCount === 1 ? "1 item" : totalAttachmentsCount + " items"
|
||||
}
|
||||
if (totalBytes > 0) {
|
||||
return formatBytes(totalBytes)
|
||||
}
|
||||
return "Shared items"
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
let value = Number(bytes)
|
||||
if (!isFinite(value) || value <= 0) {
|
||||
return ""
|
||||
}
|
||||
const units = ["B", "KB", "MB", "GB", "TB"]
|
||||
let unitIndex = 0
|
||||
while (value >= 1024 && unitIndex < units.length - 1) {
|
||||
value /= 1024
|
||||
unitIndex += 1
|
||||
}
|
||||
return (unitIndex === 0 ? Math.round(value) : value.toFixed(value >= 10 ? 1 : 2)) + " " + units[unitIndex]
|
||||
}
|
||||
|
||||
function statusText() {
|
||||
if (status === "kStartFailed") {
|
||||
return "Transfer failed"
|
||||
}
|
||||
if (status === "kConnecting") {
|
||||
return direction === "send" ? "Connecting" : "Preparing transfer"
|
||||
}
|
||||
if (status === "kAwaitingRemoteAcceptance") {
|
||||
return "Waiting for receiver"
|
||||
}
|
||||
if (status === "kInProgress") {
|
||||
const verb = direction === "send" ? "Sending" : "Receiving"
|
||||
if (totalAttachmentsCount > 0) {
|
||||
return verb + " " + Math.min(transferredAttachmentsCount + 1, totalAttachmentsCount) + " of " + totalAttachmentsCount + " items"
|
||||
}
|
||||
return verb
|
||||
}
|
||||
if (status === "kComplete") {
|
||||
return direction === "send" ? "Sent" : "Received"
|
||||
}
|
||||
if (status === "kReject") {
|
||||
return "Rejected"
|
||||
}
|
||||
if (status === "kCancelled") {
|
||||
return "Cancelled"
|
||||
}
|
||||
if (status === "kTimedOut") {
|
||||
return "Timed out"
|
||||
}
|
||||
if (isFinalStatus) {
|
||||
return "Transfer failed"
|
||||
}
|
||||
return direction === "send" ? "Starting send" : "Preparing transfer"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 24
|
||||
color: "#FFFFFF"
|
||||
border.color: "#BCE5F5"
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.compact ? 16 : 28
|
||||
spacing: root.compact ? 16 : 24
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: root.compact ? 72 : 96
|
||||
Layout.preferredHeight: root.compact ? 72 : 96
|
||||
radius: root.compact ? 18 : 24
|
||||
color: "#E8F7FC"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: root.compact ? 44 : 60
|
||||
height: root.compact ? 44 : 60
|
||||
source: "qrc:/icons/file.svg"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
smooth: true
|
||||
antialiasing: true
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: root.compact ? 5 : 10
|
||||
|
||||
Text {
|
||||
text: root.targetname
|
||||
font.pointSize: root.compact ? 14 : 17
|
||||
font.weight: 700
|
||||
color: "#1A1C1E"
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.baseName(root.filename)
|
||||
color: "#57707A"
|
||||
font.pointSize: root.compact ? 10 : 12
|
||||
elide: Text.ElideMiddle
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
id: transferProgress
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 1
|
||||
value: Math.max(0, Math.min(1, root.progressValue))
|
||||
|
||||
background: Rectangle {
|
||||
implicitHeight: root.compact ? 8 : 10
|
||||
radius: root.compact ? 4 : 5
|
||||
color: "#DDEEF5"
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
Rectangle {
|
||||
width: transferProgress.visualPosition * parent.width
|
||||
height: root.compact ? 8 : 10
|
||||
radius: root.compact ? 4 : 5
|
||||
color: "#0D6D90"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: root.statusText()
|
||||
color: "#57707A"
|
||||
font.pointSize: root.compact ? 10 : 12
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
text: Math.round(Math.max(0, Math.min(1, root.progressValue)) * 100) + "%"
|
||||
color: "#0D6D90"
|
||||
font.pointSize: root.compact ? 11 : 13
|
||||
font.weight: 700
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
id: actionButton
|
||||
visible: !root.isFinalStatus || root.finalActionText.length > 0
|
||||
enabled: root.isFinalStatus || root.cancelEnabled
|
||||
text: root.isFinalStatus ? root.finalActionText : "Cancel"
|
||||
opacity: enabled ? 1 : 0.55
|
||||
onClicked: {
|
||||
if (root.isFinalStatus) {
|
||||
root.finalActionRequested()
|
||||
} else {
|
||||
root.cancelRequested()
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
text: actionButton.text
|
||||
font.pointSize: root.compact ? 12 : 14
|
||||
font.weight: 600
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: root.compact ? 100 : 118
|
||||
implicitHeight: root.compact ? 36 : 46
|
||||
radius: root.compact ? 10 : 12
|
||||
color: root.isFinalStatus
|
||||
? (actionButton.hovered ? "#195871" : "#06384C")
|
||||
: (actionButton.hovered ? "#B62828" : "#D94C4C")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,6 +265,8 @@ void ShareTransferModel::PrepareOutgoingTransfer(
|
||||
|
||||
transfers_[row].receive_mode = false;
|
||||
transfers_[row].target = target;
|
||||
transfers_[row].transfer.reset();
|
||||
transfers_[row].total_bytes = 0;
|
||||
transfers_[row].local_path = local_path;
|
||||
EmitRowChanged(row);
|
||||
}
|
||||
@@ -394,26 +396,42 @@ void Backend::startDiscovery() {
|
||||
SetDesiredMode(Mode::kDiscovery);
|
||||
}
|
||||
|
||||
void Backend::sendFile(qint64 share_target_id, const QString& path) {
|
||||
bool Backend::sendFile(qint64 share_target_id, const QString& path) {
|
||||
if (service_ == nullptr || shutting_down_) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
const QString local_path = NormalizeLocalPath(path);
|
||||
const std::string native_path = local_path.toStdString();
|
||||
std::error_code error;
|
||||
if (!std::filesystem::is_regular_file(native_path, error)) {
|
||||
qWarning() << "Send file failed: path is not a regular file" << local_path;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
const auto target = targets_.FindTarget(share_target_id);
|
||||
if (!target.has_value()) {
|
||||
qWarning() << "Send file failed: unknown target" << share_target_id;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
transfers_.PrepareOutgoingTransfer(share_target_id, local_path, target);
|
||||
service_->SendAttachments(share_target_id, CreateFileAttachments(native_path),
|
||||
StatusCallback(QStringLiteral("Send file")));
|
||||
QPointer<Backend> backend(this);
|
||||
service_->SendAttachments(
|
||||
share_target_id, CreateFileAttachments(native_path),
|
||||
[backend, share_target_id](NearbySharingService::StatusCodes status) {
|
||||
if (!backend) {
|
||||
return;
|
||||
}
|
||||
PostStatus(backend, [backend, share_target_id, status]() {
|
||||
if (!backend || backend->shutting_down_) {
|
||||
return;
|
||||
}
|
||||
backend->ReportStatus(QStringLiteral("Send file"), status);
|
||||
if (status != NearbySharingService::StatusCodes::kOk) {
|
||||
emit backend->outgoingTransferStartFailed(share_target_id);
|
||||
}
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void Backend::accept(qint64 share_target_id) {
|
||||
|
||||
@@ -118,7 +118,7 @@ class Backend : public QObject,
|
||||
|
||||
Q_INVOKABLE void startReceive();
|
||||
Q_INVOKABLE void startDiscovery();
|
||||
Q_INVOKABLE void sendFile(qint64 share_target_id, const QString& path);
|
||||
Q_INVOKABLE bool sendFile(qint64 share_target_id, const QString& path);
|
||||
Q_INVOKABLE void accept(qint64 share_target_id);
|
||||
Q_INVOKABLE void reject(qint64 share_target_id);
|
||||
Q_INVOKABLE void cancel(qint64 share_target_id);
|
||||
@@ -126,6 +126,7 @@ class Backend : public QObject,
|
||||
|
||||
signals:
|
||||
void incomingTransfer(qint64 share_target_id);
|
||||
void outgoingTransferStartFailed(qint64 share_target_id);
|
||||
|
||||
private:
|
||||
using NearbySharingService = nearby::sharing::NearbySharingService;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<file>ShareTarget.qml</file>
|
||||
<file>SearchingDevices.qml</file>
|
||||
<file>IncomingShare.qml</file>
|
||||
<file>TransferCard.qml</file>
|
||||
<file>Targets.qml</file>
|
||||
<file>googlesans_var.ttf</file>
|
||||
<file>icons/file.svg</file>
|
||||
|
||||
Reference in New Issue
Block a user