diff --git a/MODULE.bazel b/MODULE.bazel index 5bfd7f6e..54f32991 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,7 +14,19 @@ bazel_dep(name = "rules_foreign_cc", version = "0.15.1") # for linux TUI # Should update to 7.0.0 bazel_dep(name = "ftxui", version = "6.1.9") +bazel_dep(name = "rules_qt", version = "0.0.6") +qt = use_extension("@rules_qt//extension:qt.bzl", "fetch") +qt.install( + name = "qt_linux_x86_64", + build_file = "@rules_qt//extension:qt/6.8.3/linux_x86_64.BUILD", + os = "linux", + version = "6.8.3", +) +use_repo(qt, "qt_linux_x86_64", "qt_mac_aarch64", "qt_windows_x86_64") +register_toolchains( + "@rules_qt//tools:all", +) git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 382d442e..928793bb 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -154,8 +154,8 @@ "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2", - "https://bcr.bazel.build/modules/rules_java/9.0.3/MODULE.bazel": "1f98ed015f7e744a745e0df6e898a7c5e83562d6b759dfd475c76456dda5ccea", - "https://bcr.bazel.build/modules/rules_java/9.0.3/source.json": "b038c0c07e12e658135bbc32cc1a2ded6e33785105c9d41958014c592de4593e", + "https://bcr.bazel.build/modules/rules_java/9.1.0/MODULE.bazel": "ee63f27e36a3fada80342869361182f120a9819c74320e8e65b1e04ba0cd7a9d", + "https://bcr.bazel.build/modules/rules_java/9.1.0/source.json": "da589573c1dee2c9ac4a568b301269a2e8191110ff0345c1a959fa7ea6c4dfd6", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", @@ -193,6 +193,8 @@ "https://bcr.bazel.build/modules/rules_python/1.7.0/MODULE.bazel": "d01f995ecd137abf30238ad9ce97f8fc3ac57289c8b24bd0bf53324d937a14f8", "https://bcr.bazel.build/modules/rules_python/2.0.1/MODULE.bazel": "294fbfe4dc3b24a925172e5487e1e8d3c2927cf4fba1eaddd7bad0ce50701da1", "https://bcr.bazel.build/modules/rules_python/2.0.1/source.json": "f2bacc75d3fc496dfd3e6a5986200b10e70169aaaf4ee5f1cea573c8d347e7c4", + "https://bcr.bazel.build/modules/rules_qt/0.0.6/MODULE.bazel": "09cb849f32c9ac2938a7096224a144f7932028e3166c29ded3465691e1311946", + "https://bcr.bazel.build/modules/rules_qt/0.0.6/source.json": "52749cf46b3acd342c2ce46c4ee181f7a943de347eb8b058ff163e1024408162", "https://bcr.bazel.build/modules/rules_rust/0.68.1/MODULE.bazel": "8d3332ef4079673385eb81f8bd68b012decc04ac00c9d5a01a40eff90301732c", "https://bcr.bazel.build/modules/rules_rust/0.68.1/source.json": "3378e746f81b62457fdfd37391244fa8ff075ba85c05931ee4f3a20ac1efe963", "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", @@ -222,7 +224,7 @@ "moduleExtensions": { "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "ABI1D/sbS1ovwaW/kHDoj8nnXjQ0oKU9fzmzEG4iT8o=", + "bzlTransitiveDigest": "Ga4z8lQy1YQ5rAMy+dOl0dqcCEBnYNCXku8x3YQmDZI=", "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", "recordedInputs": [ "REPO_MAPPING:rules_kotlin+,bazel_tools bazel_tools" @@ -307,6 +309,49 @@ } } }, + "@@rules_qt+//extension:qt.bzl%fetch": { + "general": { + "bzlTransitiveDigest": "+wmXAn/rrcsNKiW6OUivYSeVfd3AXW5c+xNYlBdh9q4=", + "usagesDigest": "auA61WJmCe5oSTNXyylRfl0UXq4bzk5bNKFXIoW1/mI=", + "recordedInputs": [ + "REPO_MAPPING:,rules_qt rules_qt+", + "REPO_MAPPING:rules_qt+,bazel_skylib bazel_skylib+", + "REPO_MAPPING:rules_qt+,rules_qt rules_qt+" + ], + "generatedRepoSpecs": { + "qt_linux_x86_64": { + "repoRuleId": "@@rules_qt+//extension:download_qt.bzl%download_qt", + "attributes": { + "version": "6.8.3", + "os": "linux", + "target_sdk": "desktop", + "build_file": "@@rules_qt+//extension:qt/6.8.3/linux_x86_64.BUILD", + "windows_architecture": "" + } + }, + "qt_windows_x86_64": { + "repoRuleId": "@@rules_qt+//extension:download_qt.bzl%download_qt", + "attributes": { + "version": "6.8.3", + "os": "windows", + "target_sdk": "desktop", + "build_file": "@@rules_qt+//extension:qt/6.8.3/windows_x86_64.BUILD", + "windows_architecture": "win64_msvc2022" + } + }, + "qt_mac_aarch64": { + "repoRuleId": "@@rules_qt+//extension:download_qt.bzl%download_qt", + "attributes": { + "version": "6.8.3", + "os": "macos", + "target_sdk": "desktop", + "build_file": "@@rules_qt+//extension:qt/6.8.3/mac_aarch64.BUILD", + "windows_architecture": "" + } + } + } + } + }, "@@rules_rust+//crate_universe:extension.bzl%crate": { "general": { "bzlTransitiveDigest": "RJpzElnJclpivxfYqzUhF/ChsQulhe5qCHaGylfu3SA=", diff --git a/sharing/linux/app/AppContent.qml b/sharing/linux/app/AppContent.qml index fbd1238c..0b8c7c4c 100644 --- a/sharing/linux/app/AppContent.qml +++ b/sharing/linux/app/AppContent.qml @@ -4,12 +4,20 @@ import QtQuick.Controls import QtQuick.Shapes RowLayout { - id: top + id: top anchors.fill: parent spacing: 0 property string pendingPath: "" property bool pendingTransfer: false + property bool incomingShare: false + property int currentIndex: 0 + + property string filename: "VacationPhoto_2026.jpg" + property string targetname: "Lasan's A55" + property real progressValue: 0.64 + property string statusText: "Receiving 1 of 1 items" + property bool transferring: true Connections { target: backend @@ -17,23 +25,51 @@ RowLayout { function onStatusTextChanged(){ console.log(backend.statusText) } + function onIncomingTransfer(share_target_id, device_name, status){ + console.log("Getting incoming share") + currentIndex = 2 + transferring = false + } + } + Component.onCompleted: { + backend.startReceive() + } + Component.onDestruction: { + backend.stopReceive() + backend.stopDiscovery() } - //Component.onCompleted: { - // backend.startReceive(); - //} - //Component.onDestruction: { - // backend.stopReceive(); - //} - Sidebar {} + function cancelPendingShare(){ + pendingPath = "" + currentIndex = 0 + backend.stopDiscovery() + backend.startReceive() + } + + function startSharing(){ + backend.stopReceive() + backend.startDiscovery() + currentIndex = 1 + } + + Sidebar { + pendingPath: top.pendingPath + onCancelPendingShareRequested: { + top.cancelPendingShare() + } + } StackLayout { id: contentStack Layout.fillWidth: true Layout.fillHeight: true - currentIndex: 0 + currentIndex: top.currentIndex Drop { + onFileSelected: path =>{ + top.pendingPath = path + startSharing() + } } Rectangle { @@ -45,11 +81,11 @@ RowLayout { } IncomingShare { - filename: "VacationPhoto_2026.jpg" - targetname: "Lasan's A55" - progressValue: 0.64 - statusText: "Receiving 1 of 1 items" - transferring: true + filename: top.filename + targetname: top.targetname + progressValue: top.progressValue + statusText: top.statusText + transferring: top.transferring } diff --git a/sharing/linux/app/Drop.qml b/sharing/linux/app/Drop.qml index 4f1fc7a5..08769b97 100644 --- a/sharing/linux/app/Drop.qml +++ b/sharing/linux/app/Drop.qml @@ -2,11 +2,38 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls import QtQuick.Shapes +import QtQuick.Dialogs +import QtCore Rectangle { + signal fileSelected(string filePath) + id: dropZone color: "transparent" - + FileDialog { + id: fileDialog + title: "Please choose a file" + currentFolder: Qt.resolvedUrl(StandardPaths.writableLocation(StandardPaths.HomeLocation)) + + // Optional: Filter for specific files (e.g., images, PDFs) + nameFilters: ["All files (*)"] + + // Handle the selected file + onAccepted: { + fileSelected(fileDialog.selectedFile) + // Your logic to process/upload the file goes here + } + onRejected: { + console.log("File picker canceled") + } + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor // Changes cursor to a hand on hover + onClicked: { + fileDialog.open() // Opens the native OS file picker + } + } //signal fileDropped(string path) DropArea { anchors.fill: parent @@ -15,12 +42,13 @@ Rectangle { dropText.text = "Drag and drop files to share"; dropZone.color = "transparent"; } + onEntered: k => { console.log("Entered")} // The action: What happens when the user releases the item here onDropped: drop => { - drop.acceptProposedAction(); + drop.acceptProposedAction(); if (drop.hasUrls) { console.log(drop.urls[0].toString()); for (let i of drop.formats){ @@ -50,6 +78,16 @@ Rectangle { font.pointSize: 18 color: "#333333" } + Text { + id: subText + text: "Click to open file picker" + font.pointSize: 12 + color: "gray" + horizontalAlignment: Text.AlignHCenter + Layout.fillWidth: true + } + + } Canvas { id: dashedBorderCanvas diff --git a/sharing/linux/app/ShareTarget.qml b/sharing/linux/app/ShareTarget.qml index 734e4efa..ab099641 100644 --- a/sharing/linux/app/ShareTarget.qml +++ b/sharing/linux/app/ShareTarget.qml @@ -1,18 +1,16 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls -import QtQuick.Shapes Rectangle { id: rootItem // Explicit sizes so parent containers know how to space them width: 100 - height: 130 + height: 110 // Reduced slightly since the progress outer bounds are gone color: "transparent" // --- CUSTOM ARGUMENTS (PROPERTIES) --- property string deviceName: "Unknown Device" - property real progressValue: 0.0 // Value between 0.0 and 1.0 property string iconSource: "qrc:icons/smartphone.svg" ColumnLayout { @@ -21,85 +19,29 @@ Rectangle { anchors.right: parent.right spacing: 5 - // --- ICON CONTAINER WITH PROGRESS RINGS --- + // --- ICON CONTAINER --- Item { id: iconContainer - implicitWidth: 72 - implicitHeight: 72 + implicitWidth: 60 + implicitHeight: 60 Layout.alignment: Qt.AlignHCenter - // Target angle calculated using the custom dynamic argument - property real targetAngle: 360 * rootItem.progressValue - - Behavior on targetAngle { - NumberAnimation { - duration: 300 - easing.type: Easing.OutQuad - } - } - - // Background Gray Track Ring - Shape { - anchors.fill: parent - layer.enabled: true - layer.samples: 8 - antialiasing: true - - ShapePath { - strokeColor: "#E0E4E8" - strokeWidth: 4 - fillColor: "transparent" - - PathAngleArc { - centerX: iconContainer.width / 2 - centerY: iconContainer.height / 2 - radiusX: 32 - radiusY: 32 - startAngle: -90 - sweepAngle: 360 - } - } - } - - // Active Progress Ring - Shape { - anchors.fill: parent - layer.enabled: true - layer.samples: 8 - antialiasing: true - - ShapePath { - strokeColor: "#00658F" - strokeWidth: 4 - fillColor: "transparent" - capStyle: ShapePath.RoundCap - - PathAngleArc { - centerX: iconContainer.width / 2 - centerY: iconContainer.height / 2 - radiusX: 32 - radiusY: 32 - startAngle: -90 - sweepAngle: iconContainer.targetAngle - } - } - } - - // Icon Circle + // Icon Circle Background Rectangle { - width: 60 - height: 60 - anchors.centerIn: parent + id: iconCircle + anchors.fill: parent color: "#6EA7B6" - radius: 30 + radius: width / 2 Button { + id: iconButton icon.source: rootItem.iconSource anchors.fill: parent anchors.margins: 5 icon.color: "white" - icon.height: height - icon.width: width + icon.height: iconCircle.height + icon.width: iconCircle.width + background: Rectangle { color: "transparent" } diff --git a/sharing/linux/app/Sidebar.qml b/sharing/linux/app/Sidebar.qml index ff265fff..5e8509c6 100644 --- a/sharing/linux/app/Sidebar.qml +++ b/sharing/linux/app/Sidebar.qml @@ -8,8 +8,12 @@ Rectangle { Layout.fillHeight: true Layout.preferredWidth: 300 + property string pendingPath: "" + color: "#CBF0FF" + signal cancelPendingShareRequested() + ColumnLayout { anchors.margins: 20 anchors.fill: parent @@ -65,6 +69,7 @@ Rectangle { } Rectangle { + visible: pendingPath.length != 0 Layout.preferredHeight: 350 Layout.fillWidth: true border.color: "#91C8DE" @@ -98,13 +103,14 @@ Rectangle { Layout.fillWidth: true color: "gray" wrapMode: Text.WordWrap - text: "/home/lasan/test/this/is/a/very/long/file/path/file.pp" + text: decodeURIComponent(pendingPath).replace("file://", "") horizontalAlignment: Text.AlignHCenter } Button { id: cancelbutton Layout.fillWidth: true text: "Cancel" + onClicked: sidebar.cancelPendingShareRequested() contentItem: Text { text: cancelbutton.text font.pointSize: 14