mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
added global event bus. refactored targets screen
This commit is contained in:
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Shapes
|
||||
import "."
|
||||
|
||||
RowLayout {
|
||||
id: top
|
||||
@@ -31,13 +32,23 @@ RowLayout {
|
||||
transferring = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: EventBus
|
||||
|
||||
function onFileSelected(path) {
|
||||
top.pendingPath = path;
|
||||
startSharing();
|
||||
}
|
||||
|
||||
function onCancelPendingShareRequested() {
|
||||
top.cancelPendingShare();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
backend.startReceive();
|
||||
}
|
||||
Component.onDestruction: {
|
||||
backend.stopReceive();
|
||||
backend.stopDiscovery();
|
||||
}
|
||||
|
||||
function cancelPendingShare() {
|
||||
pendingPath = "";
|
||||
@@ -54,9 +65,6 @@ RowLayout {
|
||||
|
||||
Sidebar {
|
||||
pendingPath: top.pendingPath
|
||||
onCancelPendingShareRequested: {
|
||||
top.cancelPendingShare();
|
||||
}
|
||||
}
|
||||
StackLayout {
|
||||
id: contentStack
|
||||
@@ -64,97 +72,9 @@ RowLayout {
|
||||
Layout.fillHeight: true
|
||||
currentIndex: top.currentIndex
|
||||
|
||||
Drop {
|
||||
onFileSelected: path => {
|
||||
top.pendingPath = path;
|
||||
startSharing();
|
||||
}
|
||||
}
|
||||
Drop {}
|
||||
|
||||
Rectangle {
|
||||
color: "transparent"
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Rectangle {
|
||||
id: pulseButton
|
||||
property real pulseSize: Math.min(parent.width, parent.height) * 0.5
|
||||
|
||||
width: pulseSize
|
||||
height: pulseSize
|
||||
radius: pulseSize / 2
|
||||
color: "#bde8f9"
|
||||
anchors.centerIn: parent
|
||||
|
||||
SequentialAnimation {
|
||||
id: pulseAnimation
|
||||
loops: Animation.Infinite
|
||||
running: true
|
||||
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
target: pulseButton
|
||||
property: "scale"
|
||||
from: 1.0
|
||||
to: 1.5
|
||||
duration: 1000
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: pulseButton
|
||||
property: "opacity"
|
||||
from: 1.0
|
||||
to: 0
|
||||
duration: 1000
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
target: pulseButton
|
||||
property: "scale"
|
||||
from: 1.5
|
||||
to: 1.0
|
||||
duration: 1000
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: pulseButton
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1.0
|
||||
duration: 1000
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 64
|
||||
spacing: 40
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: "Searching for devices..."
|
||||
font.pointSize: 24
|
||||
font.weight: 600
|
||||
color: "#377B95"
|
||||
}
|
||||
}
|
||||
|
||||
Targets {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
SearchingDevices {}
|
||||
|
||||
IncomingShare {
|
||||
filename: top.filename
|
||||
|
||||
@@ -23,10 +23,13 @@ qt_resource_via_qrc(
|
||||
qrc_file = "resources.qrc",
|
||||
files = [
|
||||
"main.qml",
|
||||
"qmldir",
|
||||
"AppContent.qml",
|
||||
"EventBus.qml",
|
||||
"Drop.qml",
|
||||
"Sidebar.qml",
|
||||
"ShareTarget.qml",
|
||||
"SearchingDevices.qml",
|
||||
"Targets.qml",
|
||||
"IncomingShare.qml",
|
||||
"googlesans_var.ttf",
|
||||
|
||||
@@ -4,10 +4,10 @@ import QtQuick.Controls
|
||||
import QtQuick.Shapes
|
||||
import Qt.labs.platform as Platform
|
||||
import QtCore
|
||||
import "."
|
||||
|
||||
Rectangle {
|
||||
id: dropZone
|
||||
signal fileSelected(string filePath)
|
||||
color: "transparent"
|
||||
Platform.FileDialog {
|
||||
id: fileDialog
|
||||
@@ -17,13 +17,12 @@ Rectangle {
|
||||
nameFilters: ["All files (*)"]
|
||||
|
||||
onAccepted: {
|
||||
fileSelected(fileDialog.file);
|
||||
EventBus.fileSelected(fileDialog.file);
|
||||
}
|
||||
onRejected: {
|
||||
console.log("File picker canceled");
|
||||
}
|
||||
}
|
||||
//signal fileDropped(string path)
|
||||
DropArea {
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
signal fileSelected(string filePath)
|
||||
signal cancelPendingShareRequested()
|
||||
signal shareTargetSelected(var shareTargetId)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
|
||||
Rectangle {
|
||||
color: "transparent"
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Rectangle {
|
||||
id: pulseButton
|
||||
property real pulseSize: Math.min(parent.width, parent.height) * 0.5
|
||||
|
||||
width: pulseSize
|
||||
height: pulseSize
|
||||
radius: pulseSize / 2
|
||||
color: "#bde8f9"
|
||||
anchors.centerIn: parent
|
||||
|
||||
SequentialAnimation {
|
||||
id: pulseAnimation
|
||||
loops: Animation.Infinite
|
||||
running: true
|
||||
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
target: pulseButton
|
||||
property: "scale"
|
||||
from: 0.75
|
||||
to: 1.5
|
||||
duration: 600
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: pulseButton
|
||||
property: "opacity"
|
||||
from: 1.0
|
||||
to: 0
|
||||
duration: 600
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
target: pulseButton
|
||||
property: "scale"
|
||||
from: 1.5
|
||||
to: 0.75
|
||||
duration: 1500
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: pulseButton
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1.0
|
||||
duration: 1500
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 64
|
||||
spacing: 40
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: "Searching for devices..."
|
||||
font.pointSize: 24
|
||||
font.weight: 600
|
||||
color: "#377B95"
|
||||
}
|
||||
}
|
||||
|
||||
Targets {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import "."
|
||||
|
||||
Rectangle {
|
||||
id: rootItem
|
||||
@@ -12,18 +13,8 @@ Rectangle {
|
||||
// --- CUSTOM ARGUMENTS (PROPERTIES) ---
|
||||
property string deviceName: "Unknown Device"
|
||||
property string iconSource: "qrc:icons/smartphone.svg"
|
||||
property string shareTargetId: "1"
|
||||
|
||||
signal clickShareTarget()
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
rootItem.clickShareTarget(rootItem.shareTargetId)
|
||||
}
|
||||
}
|
||||
property var shareTargetId: 0
|
||||
property bool hovered: targetMouseArea.containsMouse
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
@@ -31,6 +22,7 @@ Rectangle {
|
||||
anchors.right: parent.right
|
||||
spacing: 5
|
||||
|
||||
|
||||
// --- ICON CONTAINER ---
|
||||
Item {
|
||||
id: iconContainer
|
||||
@@ -38,13 +30,21 @@ Rectangle {
|
||||
implicitHeight: 60
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
|
||||
// Icon Circle Background
|
||||
Rectangle {
|
||||
id: iconCircle
|
||||
anchors.fill: parent
|
||||
color: "#6EA7B6"
|
||||
color: rootItem.hovered ? "#195871" : "#6EA7B6"
|
||||
radius: width / 2
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 80
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: iconButton
|
||||
icon.source: rootItem.iconSource
|
||||
@@ -59,6 +59,16 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: targetMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
EventBus.shareTargetSelected(rootItem.shareTargetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TEXT COMPONENT ---
|
||||
@@ -67,7 +77,15 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
color: "#1A1C1E" // Using the text charcoal color from your palette
|
||||
color: rootItem.hovered ? "#06384C" : "#1A1C1E"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Shapes
|
||||
import "."
|
||||
|
||||
Rectangle {
|
||||
id: sidebar
|
||||
@@ -12,8 +13,6 @@ Rectangle {
|
||||
|
||||
color: "#CBF0FF"
|
||||
|
||||
signal cancelPendingShareRequested()
|
||||
|
||||
ColumnLayout {
|
||||
anchors.margins: 20
|
||||
anchors.fill: parent
|
||||
@@ -110,7 +109,7 @@ Rectangle {
|
||||
id: cancelbutton
|
||||
Layout.fillWidth: true
|
||||
text: "Cancel"
|
||||
onClicked: sidebar.cancelPendingShareRequested()
|
||||
onClicked: EventBus.cancelPendingShareRequested()
|
||||
contentItem: Text {
|
||||
text: cancelbutton.text
|
||||
font.pointSize: 14
|
||||
|
||||
@@ -12,22 +12,27 @@ Row {
|
||||
id: dummyTargets
|
||||
|
||||
ListElement {
|
||||
targetId: 1
|
||||
deviceName: "Lasan's Pixel"
|
||||
type: 1
|
||||
}
|
||||
ListElement {
|
||||
targetId: 2
|
||||
deviceName: "Work Laptop"
|
||||
type: 2
|
||||
}
|
||||
ListElement {
|
||||
targetId: 3
|
||||
deviceName: "Kitchen Tablet"
|
||||
type: 3
|
||||
}
|
||||
ListElement {
|
||||
targetId: 4
|
||||
deviceName: "A55"
|
||||
type: 1
|
||||
}
|
||||
ListElement {
|
||||
targetId: 5
|
||||
deviceName: "Long Device Name For Wrapping"
|
||||
type: 2
|
||||
}
|
||||
@@ -48,6 +53,7 @@ Row {
|
||||
spacing: 5
|
||||
|
||||
ShareTarget {
|
||||
shareTargetId: shareTargetsRow.useDummyTargets ? model.targetId : model.id
|
||||
deviceName: model.deviceName
|
||||
iconSource: {
|
||||
if (model.type === 2)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QFileSystemWatcher>
|
||||
@@ -5,6 +11,7 @@
|
||||
#include <QApplication>
|
||||
#include <QMetaObject>
|
||||
#include <QObject>
|
||||
#include <QSocketNotifier>
|
||||
#include <QTimer>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
@@ -18,6 +25,15 @@ constexpr char kQmlSourceDirEnv[] = "NEARBY_QML_SOURCE_DIR";
|
||||
constexpr char kDefaultQmlSourceDir[] =
|
||||
"/home/lasan/Dev/nearby_latest/sharing/linux/app";
|
||||
|
||||
int g_signal_pipe[2] = {-1, -1};
|
||||
|
||||
void HandleTerminationSignal(int signal) {
|
||||
const char value = static_cast<char>(signal);
|
||||
if (g_signal_pipe[1] != -1) {
|
||||
static_cast<void>(write(g_signal_pipe[1], &value, sizeof(value)));
|
||||
}
|
||||
}
|
||||
|
||||
bool IsHotReloadEnabled() {
|
||||
const QByteArray value = qgetenv(kQmlHotReloadEnv);
|
||||
return value == "1" || value.toLower() == "true";
|
||||
@@ -65,12 +81,56 @@ bool RequiresFullReload(const QFileInfo& changed_file) {
|
||||
return changed_file.fileName() == QStringLiteral("main.qml");
|
||||
}
|
||||
|
||||
void InstallTerminationCleanup(QApplication& app, Backend& backend) {
|
||||
auto cleanup_done = std::make_shared<bool>(false);
|
||||
const auto cleanup = [&backend, cleanup_done]() {
|
||||
if (*cleanup_done) {
|
||||
return;
|
||||
}
|
||||
*cleanup_done = true;
|
||||
backend.stopDiscovery();
|
||||
backend.stopReceive();
|
||||
};
|
||||
|
||||
QObject::connect(&app, &QCoreApplication::aboutToQuit, &app, cleanup);
|
||||
|
||||
if (pipe(g_signal_pipe) != 0) {
|
||||
return;
|
||||
}
|
||||
fcntl(g_signal_pipe[0], F_SETFL,
|
||||
fcntl(g_signal_pipe[0], F_GETFL, 0) | O_NONBLOCK);
|
||||
fcntl(g_signal_pipe[1], F_SETFL,
|
||||
fcntl(g_signal_pipe[1], F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
auto* notifier = new QSocketNotifier(g_signal_pipe[0],
|
||||
QSocketNotifier::Read, &app);
|
||||
QObject::connect(notifier, &QSocketNotifier::activated, &app,
|
||||
[&app, notifier, cleanup](int socket) {
|
||||
notifier->setEnabled(false);
|
||||
char buffer[32];
|
||||
while (read(socket, buffer, sizeof(buffer)) > 0) {
|
||||
}
|
||||
cleanup();
|
||||
app.quit();
|
||||
});
|
||||
|
||||
struct sigaction action {};
|
||||
action.sa_handler = HandleTerminationSignal;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
sigaction(SIGINT, &action, nullptr);
|
||||
sigaction(SIGTERM, &action, nullptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
Backend backend;
|
||||
InstallTerminationCleanup(app, backend);
|
||||
|
||||
const int fontId = QFontDatabase::addApplicationFont(":/googlesans_var.ttf");
|
||||
|
||||
if (fontId != -1) {
|
||||
@@ -83,6 +143,8 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
if (IsHotReloadEnabled()) {
|
||||
engine.rootContext()->setContextProperty("backend", &backend);
|
||||
|
||||
const QDir qml_source_dir = QmlSourceDir();
|
||||
const QUrl source_url =
|
||||
QUrl::fromLocalFile(qml_source_dir.absoluteFilePath("main.qml"));
|
||||
@@ -132,10 +194,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
// 1. Create the backend instance in C++
|
||||
Backend backend;
|
||||
|
||||
// 2. Inject it into the QML root context
|
||||
engine.rootContext()->setContextProperty("backend", &backend);
|
||||
// Loaded via the qrc scheme since it's compiled into the binary
|
||||
const QUrl url(QStringLiteral("qrc:/main.qml"));
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
singleton EventBus 1.0 EventBus.qml
|
||||
@@ -1,10 +1,13 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="/">
|
||||
<file>main.qml</file>
|
||||
<file>qmldir</file>
|
||||
<file>AppContent.qml</file>
|
||||
<file>EventBus.qml</file>
|
||||
<file>Sidebar.qml</file>
|
||||
<file>Drop.qml</file>
|
||||
<file>ShareTarget.qml</file>
|
||||
<file>SearchingDevices.qml</file>
|
||||
<file>IncomingShare.qml</file>
|
||||
<file>Targets.qml</file>
|
||||
<file>googlesans_var.ttf</file>
|
||||
|
||||
Reference in New Issue
Block a user