Внутри Qt QML SwipeView, как мне убедиться, что Dials всегда обрабатывают сенсорный ввод вместо базового SwipeView?

Фон

У меня есть SwipeView с количеством страниц. Он разработан для небольшого круглого экрана диаметром 2,1 дюйма, и идея состоит в том, чтобы пролистывать влево или вправо между страницами, и каждая страница имеет один или несколько элементов пользовательского интерфейса для взаимодействия. Многие из этих страниц содержат Dial и маленькие /больше ничего. Может быть, кнопка внизу по центру.

Проблема

При касании (предполагаемое устройство ввода) горизонтальное перетаскивание внутри Dial чаще всего интерпретируется как навигация по SwipeView под ним. Очень редко он распознает, что я хочу отрегулировать циферблат.

С помощью мыши Dial, кажется, довольно надежно захватывает жест перетаскивания, так что даже горизонтальное перетаскивание (например, сверху или снизу циферблата) не позволяет непреднамеренно перемещаться между страницами в SwipeView.

Посмотрите это видео для сравнения поведения касания и мыши (я не смог загрузить его прямо в редакторе StackOverflow, поэтому надеюсь, что ссылка на imgur приемлема)

Вопрос

Есть ли способ дать Dial более высокий приоритет над интерпретацией жестов касания / пролистывания, чтобы пользователь не мог случайно пролистать между страницами? (Больше похоже на поведение мыши)

Я могу обойти это, начав жест вертикально, а затем переместившись в нужное положение, но я бы предпочел не сообщать об этом моему пользователю, и это, кажется, их наиболее частое зависание в этом приложении.

Циферблаты составляют 70% от размера самого окна, поэтому за пределами циферблатов у пользователя достаточно места, чтобы провести пальцем по SwipeView.

Что я пробовал/рассматривал до сих пор

Я пытался настроить SwipeView interactive: !(dial1.pressed || dial2.pressed) и т. д., но это не работает. Казалось бы, свойство .pressed циферблата не вступает в силу до тех пор, пока циферблат окончательно не будет выбран для обработки ввода. Я бы предпочел, чтобы в данном конкретном случае он вел себя так же, как с мышью.

При рассмотрении SwipeView, который является чисто interactive: false — так что Dial гарантированно улавливает жесты — мне пришлось бы добавить кнопки на каждый экран для навигации между страницами, и это уберет место, которое у меня есть для другого пользовательского интерфейса. элементы. Страницы набора - единственные, которые вызывают у меня проблемы. На других страницах уже есть 1-3 кнопки в местах, где мало риска конфликтного жеста. Если мне нужно добавить кнопку с любой стороны страницы, это начинает сжимать вещи до такой степени, что становится трудно попасть на предполагаемый размер экрана.

Единственная другая альтернатива, которую я могу придумать на данный момент, — это кнопка редактирования / выполнения (например, модальное диалоговое окно?), но это добавляет некоторые трения, которых я бы предпочел избежать.

Код

Этот код является минимальным воспроизводимым примером, как показано на видео. В последний раз я пробовал в Qt 6.3.0 на Ubuntu 20.04 с сенсорным экраном USB/HDMI.

import QtQuick
import QtQuick.Controls.Basic

Window {
    width: 480
    height: 480
    visible: true
    title: "SwipeView Dial Test"

    SwipeView {
        anchors.fill: parent
        Item {
            Dial {
                id: dial1
                anchors.centerIn: parent
                width: parent.width * 0.7
                height: parent.height * 0.7
            }
        }
        Item {
            Dial {
                id: dial2
                anchors.centerIn: parent
                width: parent.width * 0.7
                height: parent.height * 0.7
            }
        }
    }
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Действительно похоже, что есть проблема с обработчиком перетаскивания Dial. Такое ощущение, что не весь элемент управления покрывается обработчиком перетаскивания, поэтому события подхватываются SwipeView в неожиданное время.

Я создал обходной путь, заплатив Dial с помощью DialPatched.qml.

В моей реализации я создал макет Rectangle, который находится поверх Dial. Мнимый прямоугольник теперь будет получать события перетаскивания через свой MouseArea, который имеет preventStealing: true. Мы наблюдаем за изменениями в положении мыши и реконструируем значение с помощью Math.atan2(), чтобы преобразовать векторы в углы и вычислить новое значение для циферблата.

Чтобы увидеть макет прямоугольника, я окрасил его в почти прозрачный «красный цвет», чтобы вы могли видеть и прямоугольник, и циферблат под ним. Если вы собираетесь использовать этот подход, я рекомендую установить непрозрачность на 0 или заменить прямоугольник на элемент-заполнитель.

Кроме того, я внес небольшую поправку в ваш расчет ширины/высоты.

import QtQuick
import QtQuick.Controls
Page {
    SwipeView {
        anchors.fill: parent
        Item {
            DialPatched {
                id: dial1
                anchors.centerIn: parent
                debug: debugCheckBox.checked
                width: Math.min(parent.width,parent.height) * 0.7
                height: width
            }
        }
        Item {
            DialPatched {
                id: dial2
                anchors.centerIn: parent
                debug: debugCheckBox.checked
                width: Math.min(parent.width,parent.height) * 0.7
                height: width
            }
        }
    }
    CheckBox { id: debugCheckBox; text: "Debug"; checked: true }
}

// DialPatched.qml
import QtQuick
import QtQuick.Controls
Dial {
    id: dial
    property bool debug: true
    Item {
        anchors.fill: parent
        Rectangle {
            width: parent.width
            height: parent.height
            color: debug ? "red" : "transparent"
            opacity: debug ? 0.05 : 0
            property int startX: 0
            property int startY: 0
            onXChanged: Qt.callLater(mouseMove, startX+x, startY+y)
            onYChanged: Qt.callLater(mouseMove, startX+x, startY+y)
            MouseArea {
                anchors.fill: parent
                drag.target: parent
                preventStealing: true
                onPressed: function (mouse) {
                    parent.startX = mouse.x;
                    parent.startY = mouse.y;
                    parent.Drag.active = true;
                }
                onReleased: {
                    parent.Drag.active = false;
                    parent.x = 0;
                    parent.y = 0;
                }
            }
            function mouseMove(mx,my) {
                if (Drag.active) {
                    let ang = Math.atan2(height/2 - my, mx - width/2) * 180 / Math.PI;
                    ang = (360 + 270 - Math.round(ang)) % 360;
                    dial.value = (ang - 40) / 280 * (dial.to - dial.from) + dial.from;
                }
            }
        }
    }
}

Вы можете Попробовать онлайн!

Другие вопросы по теме