Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Ноябрь 09, 2018, 14:12



Название: Драг
Отправлено: Igors от Ноябрь 09, 2018, 14:12
Добрый день

Юзер выделяет мышью пр-к в окне (QRect). Это действие реализовано "модально" (ну почти, псевдокод)
Код
C++ (Qt)
while (MouseDown()) {
 QPoint pt = GetMousePos();
 ...
}
return selectedRect;
И этот код повторяется десятки раз (если не сотни), и пишется еще и еще. Вот какой ф-ционал обычно реализован

- ну естественно надо вернуть результат QRect (если драг не состоялся, то пустой)

- почти всегда надо рисовать пр-к драга (образец - QRubberBand)

- почти всегда есть еще пр-к "ограничитель" (выделение возможно только внутри него)

- часто (по меньшей мере в половине случаев) есть autoScroll, т.е. если мыша оказалась за пределами "ограничителя" - область просмотра скроллится по вертикали/горизонтали

 Есть ли смысл это обобщить? Если да, то как Вы видите общие классы?

Спасибо


Название: Re: Драг
Отправлено: Igors от Ноябрь 12, 2018, 09:26
И молчок :) Ну хорошо хоть нет вопросов типа "а для чего нужно выделять мышью ?". Ладно, попробуем с чего-то начать. Простецкий вариант: да написать просто ф-цию (утилиту) - и все дела. Напр
Код
C++ (Qt)
QRect ExecDragRect(
   const QPoint & startPt,   // точка начала драга
   const QRect * limitR      //  ограничивающий пр-к (can be NULL)
);
Очень неплохо, ф-ция сама разберется с мышью и с отрисовкой пр-ка (это верхнее "overlay" окно). Кстати в старом ОС подобная ф-ция была. Можно еще добавить параметров, напр "минимальный" пр-к и.т.п. - но это не принципиально.

Чего не хватает? Вот autoScroll'а никакого нет, добрая половина случаев не покрывается. Ну понятно как/что скроллить - ф-ция знать не должна. Опять пойдем по пути наименьшего сопротивления: добавим аргумент QWidget/QObject и пусть ф-ция пуляет в него сигналами. 

Что Вы об этом думаете?


Название: Re: Драг
Отправлено: Racheengel от Ноябрь 14, 2018, 16:16
Ну я бы сделал классик-контроллер драга с соответствующими полями и функционалом.
И пусть он сигналы и "пуляет", что то типа
emit dragRectChanged(QRectF newRect);


Название: Re: Драг
Отправлено: Igors от Ноябрь 14, 2018, 17:08
Ну я бы сделал классик-контроллер драга с соответствующими полями и функционалом.
И пусть он сигналы и "пуляет", что то типа
emit dragRectChanged(QRectF newRect);
Впервые слышу о классик-контроллере, просветите. Ну и вообще развейте мысль, а то так.. намек.

Да, и это Вы к молодняка переняли моду подавать структуры по значению?  :)


Название: Re: Драг
Отправлено: Авварон от Ноябрь 14, 2018, 18:06

Да, и это Вы к молодняка переняли моду подавать структуры по значению?  :)

Ну это зависит от того, что передавать. Qrect пожалуй не стоит, а вот QPoint или там QStringView - пожалуйста


Название: Re: Драг
Отправлено: Racheengel от Ноябрь 14, 2018, 20:25
Ну я бы сделал классик-контроллер драга с соответствующими полями и функционалом.
И пусть он сигналы и "пуляет", что то типа
emit dragRectChanged(QRectF newRect);
Впервые слышу о классик-контроллере, просветите. Ну и вообще развейте мысль, а то так.. намек.

Да, и это Вы к молодняка переняли моду подавать структуры по значению?  :)

Насчет параметра - это ж как бы "сигнал", а там только сигнатура важна. Тем более при асинхронном обмене лучше все таки по значению.. но не суть.

Насчет контроллера - я имел в виду, сделать отдельный класс, что то типа CRectDragController, который собственно должен обрабатывать мышиные события и отправлять в мир сигнал "рект поменялся, товарищи!" Ну наверное и рект этот самый отрисовывать, если в этом задача.

как то так...


Название: Re: Драг
Отправлено: Igors от Ноябрь 15, 2018, 05:35
Насчет контроллера - я имел в виду, сделать отдельный класс, что то типа CRectDragController, который собственно должен обрабатывать мышиные события и отправлять в мир сигнал "рект поменялся, товарищи!" Ну наверное и рект этот самый отрисовывать, если в этом задача.

как то так...
Так собсно это уже и реализовано в приведенном выше псевдокоде. MouseDown крутит вторичный цикл (processEvents) блокируя клаву и возвращает true если мышь двинулась или false если кнопка мыши отпущена. Простой драг выглядит так
Код
C++ (Qt)
QPoint startPt = GetMousePos();
QRect limitR = GetBounds();         // пр-к ограничитель
COverShape  shape(type_Rect);    // индикатор (окно) выделения          
QRect oldR = MkRect(startPt, startPt, &limitR);                          
while (MouseDown()) {
 QRect dragR = MkRect(startPt, GetMousePos(), &limitR);
 if (dragR == oldR) continue;     // ничего не изменилось
 oldR = dragR;
 shape.Adjust(dragR);
// emit dragRectChanged(dragR);  // ???
}
// выделение закончено, обрабатываем рез-т
....
 
Ну ладно, втулим сюда сигнал "рект поменялся". Что получатель будет с ним делать? Организовывать autoScroll ему все равно придется с нуля - брать текущую позицию мыша, смотреть оказалась ли она за пределами области прокрутки (и насколько), заводить (или глушить) таймер и.т.п. Уникален только сам скролл (когда пришло событие таймера), а остальной ф-ционал явно общий - куда его поселить?

Еще такой момент. Вот напр переставляются строки в дереве. Здесь "выделения" как такового нет, и логика навороченная, как-то тулить ее каллбэком в класс-контроллер мне не хочется. А вот песня с autoScroll та же самая. Надо как-то сделать чтобы "добро не пропадало"


Название: Re: Драг
Отправлено: Racheengel от Ноябрь 15, 2018, 11:34
Пусть базовый класс контроллера тогда и реализует обработку мыши и автоскролл вьюпорта.
А уже наследники его могли бы переопределить метод (вместо сигнала) типа

virtual void OnDragRegionChanged(const QRect& newRect)

и иметь в нем специфичную логику, как, например, выделение в дереве.


Название: Re: Драг
Отправлено: Igors от Ноябрь 15, 2018, 13:43
Пусть базовый класс контроллера тогда и реализует обработку мыши и автоскролл вьюпорта.
А как он его реализует? В результате скролла должна измениться стартовая точка драга (startPt) - сместиться в противоположную сторону(ы) на число "проскролленных пыкселей" - а откуда это смещение взять? Причем скролл произойдет по таймеру, MouseDown на него не среагирует.
А уже наследники его могли бы переопределить метод (вместо сигнала) типа

virtual void OnDragRegionChanged(const QRect& newRect)

и иметь в нем специфичную логику, как, например, выделение в дереве.
Ну для выделения в дереве QRect не годится - интересует подрезанная (клипом) текущая точка мыша (куда вставлям). Ну и организация классов неудобна. Хотелось бы сделать этот драг просто методом конкретного окна, там объявить на стеке контроллер чтобы делал черновую работу. А тут - наследоваться от контроллера, перекрывать виртуал, а из него опять лезть к методам окна. Коряво


Название: Re: Драг
Отправлено: Racheengel от Декабрь 15, 2018, 02:53
1. ну тут просто смотрим,  за какую из границ порта вылезли мышом, и по таймеру скролим в обратном направлении на фиксированное кол.во пикселей.

2. тут не понял, что значит контроллер на стеке.?


Название: Re: Драг
Отправлено: Igors от Декабрь 15, 2018, 18:57
2. тут не понял, что значит контроллер на стеке.?
За истекший месяц успел не только сделать, но и основательно забыть :) Что помню: нужно сделать класс гибким, в простом случае он делает все автоматом
Код
C++ (Qt)
QPoint startPt = GetMousePos();
QRect limitR = GetBounds();  
bool createDragShape = true;
CDragControl ctl(widget, startPt, limitR, createDragShape);  // вот этот контроллер на стеке
QRect selection = ctl.Exec();
Возвращает выделенный rect. В более сложном случае берем драг на себя, а контроллер используем для аутоскролла
Код
C++ (Qt)
CDragControl ctl(widget, startPt, limitR, createDragShape);  
while (MouseDown()) {
 QPoint pt = GetMousePos();
 bool changed = ctl.Update(pt);
 if (changed) {
  ...
}
 
Метод CDragControl::Update( QPoint & pt )  разберется с текущей позицией мыша, загонит ее в пр-к ограничитель и отскроллит если надо. Ну конечно "ретроспективно" (есть такое вумное словечко) это кажется совсем простым  :)

1. ну тут просто смотрим,  за какую из границ порта вылезли мышом, и по таймеру скролим в обратном направлении на фиксированное кол.во пикселей.
А откуда возьмете "кол-во пикселей"? Как минимум нужен скроллбар (а его часто и нет, скролл = пробел + драг) + шаг скролла (напр может скроллиться по айтемам). Собсно я тоже ничего не придумал и решил не связываться ни с какими обобщениями. Для каждого конкретного окна пишу ф-цию
Код
C++ (Qt)
virtual bool СDragControl::AutoScroll( QWidget * widget, int dx, int dy);
Где dx/dy - насколько мышь вылез за пределы. В ф-ции выходит до 20 строк. Ну и наследуюсь от  СDragControl.

Мда, что-то не густо с "логикой гуя", не густо..