88 constexpr auto dataTypeMaxLength = 128;
90 auto dataTypeCount = getDragEventDataTypeCount(dragEvent);
91 if (dataTypeCount == 0)
94 std::vector<std::array<
char, dataTypeMaxLength + 1>> dataTypesStringData;
95 dataTypesStringData.resize(dataTypeCount);
97 std::vector<
char *> dataTypesStringPointers;
98 for (
std::int32_t i = 0; i < dataTypeCount; ++i)
99 dataTypesStringPointers.push_back(dataTypesStringData[i].data());
101 QArkUi::callArkUiOrFailOnErrorResult(
102 Q_OHOS_NAMED_FUNC(::OH_ArkUI_DragEvent_GetDataTypes),
103 dragEvent, dataTypesStringPointers.data(), dataTypesStringPointers.size(), dataTypeMaxLength);
105 return {dataTypesStringPointers.begin(), dataTypesStringPointers.end()};
141 static const std::pair<std::uint64_t, Qt::KeyboardModifier> arkUiToQtModifiersMap[] = {
142 {::ARKUI_MODIFIER_KEY_CTRL, Qt::KeyboardModifier::ControlModifier},
143 {::ARKUI_MODIFIER_KEY_SHIFT, Qt::KeyboardModifier::ShiftModifier},
144 {::ARKUI_MODIFIER_KEY_ALT, Qt::KeyboardModifier::AltModifier},
145 {::ARKUI_MODIFIER_KEY_FN, Qt::KeyboardModifier::MetaModifier},
148 Qt::KeyboardModifiers modifiers;
149 for (
const auto &mod : arkUiToQtModifiersMap)
150 modifiers.setFlag(mod.second, (modifierKeyStates & mod.first) != 0);
203 QOhosJsState &jsState, QtOhos::QThreadSafeRef<QWindow> qWindowRef,
const DragEventInfo &dragEventInfo,
204 QOhosSupplier<std::unique_ptr<QMimeData>> dropDataFactory, std::int32_t pendingDropRequestId)
206 qOhosPrintfDebug(
"%s: async processing of drop request with id=%d", Q_FUNC_INFO, pendingDropRequestId);
208 auto qtDropActionConsumer = QtOhos::moveToSharedPtr(
209 QtOhos::makeCallOnceConsumerWrapper<QOhosJsState &, Qt::DropAction>(
210 [pendingDropRequestId](QOhosJsState &, Qt::DropAction qtDropAction) {
212 "%s: got qtDropAction=%d for drop request with id=%d",
213 Q_FUNC_INFO,
static_cast<
int>(qtDropAction), pendingDropRequestId);
214 QArkUi::callArkUiOrFailOnErrorResult(
215 Q_OHOS_NAMED_FUNC(::OH_ArkUI_NotifyDragResult),
216 pendingDropRequestId,
217 qtDropAction != Qt::IgnoreAction
218 ? ::ARKUI_DRAG_RESULT_SUCCESSFUL
219 : ::ARKUI_DRAG_RESULT_FAILED);
220 QArkUi::callArkUiOrFailOnErrorResult(
221 Q_OHOS_NAMED_FUNC(::OH_ArkUI_NotifyDragEndPendingDone),
222 pendingDropRequestId);
225 constexpr auto notifyDragEndPendingTimeout = ch::milliseconds(1500);
226 QtOhos::setJsTimeout(
228 [pendingDropRequestId, qtDropActionConsumer](
const QOhosCallbackInfo &cbInfo) {
229 if ((*qtDropActionConsumer)(cbInfo.jsState(), Qt::IgnoreAction))
230 qOhosPrintfDebug(
"%s: used timeout action for drop request with id=%d", Q_FUNC_INFO, pendingDropRequestId);
232 notifyDragEndPendingTimeout);
234 qWindowRef.visitInQtThreadIfAlive(
235 [dragEventInfo, dropDataFactory = std::move(dropDataFactory), qtDropActionConsumer](QWindow &qWindow)
mutable {
236 auto dropAction = processDropInQWindow(qWindow, dragEventInfo, std::move(dropDataFactory));
237 QOhosJsThreadGateway::runAndWait(
238 [&](QOhosJsState &jsState) {
239 (*qtDropActionConsumer)(jsState, dropAction);
251 QOhosJsState &jsState, ::ArkUI_DragEvent *dragEvent, QtOhos::QThreadSafeRef<QWindow> qWindowRef,
252 const DragEventInfo &dragEventInfo, QOhosSupplier<std::unique_ptr<QMimeData>> dropDataFactory)
254 if (!isAsyncDropHandlingAllowed())
257 std::int32_t pendingDropRequestId = 0;
258 QArkUi::callArkUiOrFailOnErrorResult(
259 Q_OHOS_NAMED_FUNC(::OH_ArkUI_DragEvent_RequestDragEndPending),
260 dragEvent, &pendingDropRequestId);
261 processPendingDropRequestAsynchronously(
262 jsState, qWindowRef, dragEventInfo,
263 std::move(dropDataFactory), pendingDropRequestId);
312 const DragEventInfo &dragEventInfo, QOhosSupplier<std::unique_ptr<QMimeData>> dropDataFactory)
315 constexpr auto answerWaitTimeout = ch::milliseconds(5);
316 constexpr auto waitCooldownAfterTimeout = ch::milliseconds(200);
318 const auto requestId = ++m_lastRequestId;
320 [dragEventInfo, dropDataFactory = std::move(dropDataFactory),
321 sharedDropActionState = m_sharedDropActionState, requestId](QWindow &qWindow) {
322 QDrag *currentDrag = QDragManager::self()->object();
323 QPlatformDragQtResponse qtResponse = QWindowSystemInterface::handleDrag(
325 currentDrag !=
nullptr ? currentDrag->mimeData() : dropDataFactory().get(),
326 dragEventInfo.localDropPos,
327 currentDrag !=
nullptr ? currentDrag->supportedActions() : dragEventInfo.dropActions,
328 Qt::LeftButton, dragEventInfo.keyboardModifiers);
329 if (currentDrag !=
nullptr && qtResponse.isAccepted() && qtResponse.acceptedAction() != Qt::IgnoreAction)
333 std::lock_guard<
std::mutex> stateLock(sharedDropActionState->mutex);
334 if (requestId <= sharedDropActionState->answeredRequestId)
337 sharedDropActionState->answeredRequestId = requestId;
338 sharedDropActionState->optDropAction = qtResponse.acceptedAction();
340 sharedDropActionState->answerCondVar.notify_all();
343 m_pendingQueryUpdater(
344 [&](std::function<
void(QWindow &)> &pendingQuery) {
345 pendingQuery = std::move(queryFunc);
348 const auto now = ch::steady_clock::now();
349 const bool waitingMakesSense =
350 !m_optLastFailedWaitTime || now - m_optLastFailedWaitTime.value() >= waitCooldownAfterTimeout;
353 std::unique_lock<
std::mutex> stateLock(m_sharedDropActionState->mutex);
354 if (waitingMakesSense) {
355 const bool answered = m_sharedDropActionState->answerCondVar.wait_for(
356 stateLock, answerWaitTimeout,
358 return m_sharedDropActionState->answeredRequestId >= requestId;
361 m_optLastFailedWaitTime.reset();
363 m_optLastFailedWaitTime = now;
366 return m_sharedDropActionState->optDropAction;
380 QtOhos::QThreadSafeRef<QWindow> qWindowRef)
382 auto eventsHandler = [qWindowRef, dragMoveResponder = DragMoveResponder(qWindowRef)](
383 QOhosJsState &jsState, ::ArkUI_NodeEvent *nodeEvent)
mutable {
384 auto eventType = QArkUi::callArkUi(Q_OHOS_NAMED_FUNC(OH_ArkUI_NodeEvent_GetEventType), nodeEvent);
385 auto *dragEvent = QArkUi::callArkUiOrFailOnNullResult(Q_OHOS_NAMED_FUNC(::OH_ArkUI_NodeEvent_GetDragEvent), nodeEvent);
386 auto node = QArkUi::callArkUiOrFailOnNullResult(Q_OHOS_NAMED_FUNC(OH_ArkUI_NodeEvent_GetNodeHandle), nodeEvent);
388 auto touchDisplayPosition = getDragEventTouchDisplayPosition(dragEvent);
389 auto nodeDisplayPosition = QArkUi::Node::nodeDisplayPosition(node);
390 auto localPosition = touchDisplayPosition - nodeDisplayPosition;
392 DragEventInfo dragEventInfo = {
393 .localDropPos = localPosition,
394 .dropActions = mapQOhosArkUiDropOperationToQt(getQOhosDragEventDropOperation(dragEvent)),
395 .keyboardModifiers = mapArkUiModifierKeyStatesToQt(getDragEventModifierKeyStates(dragEvent)),
398 qOhosPrintfDebug(
"QNativeNode: got drag event: %d, (%d,%d)", eventType, dragEventInfo.localDropPos.x(), dragEventInfo.localDropPos.y());
401 case ::NODE_ON_DRAG_ENTER:
402 dragMoveResponder.forgetLastDropAction();
404 case ::NODE_ON_DRAG_MOVE:
406 auto qtDropAction = dragMoveResponder.respondToMove(
408 makeDummyQMimeDataFactoryFromUdmfDataTypes(getDragEventDataTypes(dragEvent)));
409 QArkUi::callArkUiOrFailOnErrorResult(
410 Q_OHOS_NAMED_FUNC(::qOhosSetArkUiDragEventDragResult),
412 static_cast<std::uint32_t>(
413 qtDropAction.value_or(Qt::IgnoreAction) != Qt::IgnoreAction
414 ? DropStatus::Enabled
415 : DropStatus::Disabled));
416 setDragEventSuggestedDropOperationIfAvailable(
417 dragEvent, qAndThen(qtDropAction, &tryMapQOhosArkUiDropOperationFromQt));
420 case ::NODE_ON_DRAG_LEAVE:
421 qWindowRef.visitInQtThreadIfAlive(
422 [](QWindow &qWindow) {
423 std::ignore = QWindowSystemInterface::handleDrag(
424 &qWindow,
nullptr, QPoint(), Qt::IgnoreAction, Qt::MouseButtons(), Qt::KeyboardModifiers());
429 QOhosSupplier<std::unique_ptr<QMimeData>> dropDataFactory;
430 if (getDragEventDataTypeCount(dragEvent) != 0) {
431 auto optDragUdmfData = tryGetDragEventUdmfDataOrNull(dragEvent);
432 dropDataFactory = optDragUdmfData
433 ? createQMimeDataFactoryFromUdmfData(std::move(*optDragUdmfData))
434 : &std::make_unique<QMimeData>;
436 dropDataFactory = &std::make_unique<QMimeData>;
438 auto copyableDropDataFactory = makeImplicitlySharedSupplier(std::move(dropDataFactory));
440 bool asyncProcessingStarted = tryStartAsyncProcessingOfDropEvent(
441 jsState, dragEvent, qWindowRef, dragEventInfo, copyableDropDataFactory);
443 if (!asyncProcessingStarted) {
444 auto qtDropAction = tryRunInQtThreadAndGetResult<QWindow, Qt::DropAction>(
446 [dragEventInfo, copyableDropDataFactory](QWindow &qWindow) {
447 return processDropInQWindow(qWindow, dragEventInfo, copyableDropDataFactory);
449 QArkUi::callArkUiOrFailOnErrorResult(
450 Q_OHOS_NAMED_FUNC(::OH_ArkUI_DragEvent_SetDragResult),
452 qtDropAction.value_or(Qt::IgnoreAction) != Qt::IgnoreAction
453 ? ::ARKUI_DRAG_RESULT_SUCCESSFUL
454 : ::ARKUI_DRAG_RESULT_FAILED);
455 setDragEventSuggestedDropOperationIfAvailable(
456 dragEvent, qAndThen(qtDropAction, &tryMapQOhosArkUiDropOperationFromQt));
465 return [eventsHandler = std::move(eventsHandler)](::ArkUI_NodeEvent *nodeEvent)
mutable {
466 QOhosJsThreadGateway::runAndWait(
467 [&](QOhosJsState &jsState) {
468 eventsHandler(jsState, nodeEvent);