Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: Alp от Сентябрь 11, 2009, 18:56



Название: [РЕШЕНО] Определить QRect для слова в QLabel
Отправлено: Alp от Сентябрь 11, 2009, 18:56
Надо сделать (точнее, он уже сделан, надо подумать как его улучшить) виджет на базе QLable с "активными зонами". Ну, например, какое-то слово подсвечено и при клике на нем появляется окошко редактора прямо поверх слова (то, что называют in-place редактирование), после чего юзер вбивает туда что-то и завершает работу с этим редактором (нажатие Enter, потеря фокуса - неважно). Содержимое эдита после этого замещает слово на котором был произведен клик. И второй случай - с менюшками (с ними проще)

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

Сейчас думаю как бы это попроще реализоваться средствами Qt. Пока в голову пришло QLabel + хтмл-ссылки (и подсветка и стили и передача типа поля/кода поля впо ссылке). Проблема одна: как попроще получить список прямоугольников для слов. Или хотя бы определить QRect для кликнутого слова.

С меню проблем особых нет - взял координаты клика по OnLinkActivated, показал меню, заместил нужное, показал на лейбле, то с редактирование сложнее.

Есть какие-нибудь мысли или пинки в нужном направлении?


Название: Re: Определить QRect для слова в QLabel
Отправлено: Rcus от Сентябрь 11, 2009, 19:15
Я думаю нужно скорее смотреть в сторону классов Rich Text Processing - эта часть управляет текстом в стандартных текстовых виджетах и имеет довольно развитый публичный API.


Название: Re: Определить QRect для слова в QLabel
Отправлено: SABROG от Сентябрь 11, 2009, 20:59
По идее можно подсмотреть в коде QLineEdit, он же с точностью определяет между какими буквами был сделан клик, чтобы установить курсор ввода.


Название: Re: Определить QRect для слова в QLabel
Отправлено: Alp от Сентябрь 12, 2009, 07:02
Я думаю нужно скорее смотреть в сторону классов Rich Text Processing - эта часть управляет текстом в стандартных текстовых виджетах и имеет довольно развитый публичный API.
Думаю, это будет уже оверхэд. Проще тогда использовать описанный мной алгоритм.


Название: Re: Определить QRect для слова в QLabel
Отправлено: Alp от Сентябрь 12, 2009, 07:36
По идее можно подсмотреть в коде QLineEdit, он же с точностью определяет между какими буквами был сделан клик, чтобы установить курсор ввода.
Посмотрел. Определение позиции курсора по QPoint реализуется через вызов QLineEditPrivate::xToPos(const QPoint&). А вот имплементацию этого метода в /qt/qt/src  почему-то найти не могу. Точнее, вобще не могу найти имплементацию QLineEditPrivate. Может у тебя получится?

Но в целом идея здравая. Попробую сделать невидимый QLineEdit, по точке определять позицию курсора (она же - индекс символа в тексте), и по ней определять границы слова. Затем немного магии: ставим курсов в начало слова, дергаем за cursorRect(), затем переставляем курсор в конец слова и снова дергаем за тот же метод. В итоге имеем два прямоугольника описывающих _курсор_ в начале слова и в конце. Оттуда получаем координаты нужного QRect для слова целиком.

Попробую реализовать.


Название: Re: Определить QRect для слова в QLabel
Отправлено: Rcus от Сентябрь 12, 2009, 09:00
Думаю, это будет уже оверхэд. Проще тогда использовать описанный мной алгоритм.
/* sigh */ Знаете что идея высказанная в последнем посте тоже использует классы обработки текста (QTextLayout в частности). Давайте поговорим об оверхеде, да.


Название: Re: Определить QRect для слова в QLabel
Отправлено: Alp от Сентябрь 12, 2009, 12:28
Думаю, это будет уже оверхэд. Проще тогда использовать описанный мной алгоритм.
/* sigh */ Знаете что идея высказанная в последнем посте тоже использует классы обработки текста (QTextLayout в частности). Давайте поговорим об оверхеде, да.
Почитал раздел связанный с Rich Text. Нет там ничего особенного за исключением мыслей высказанных выше.

Надо ведь не только определить "вот слово, по которому кликнули", надо еще хранить информацию о типе поля, о текстовом его содержимом. В итоге: тип входных данных тот же.
Начальная обработка текста: построение коллекции прямоугольников и типов данных для каждого из них
Обработчик клика: определить какой прямоугольник содержит точку и вывести необходимые данные.

Единственная мысль хоть как-то связанная с RT-работой - использовать не QLineEdit, QTextEdit? т.к. первый не умеет перенос строк. В остальном удобных методов получения чего надо в общем-то и нет.


Название: Re: Определить QRect для слова в QLabel
Отправлено: SABROG от Сентябрь 12, 2009, 12:36
QLineEdit использует публичный класс QTextLine. А именно метод:
int QTextLine::xToCursor ( qreal x, CursorPosition cpos = CursorBetweenCharacters ) const

Возможно даже, что все уже есть. Остается только это применить, в документации класс описан. И где-то я в QQ видел применение этого класса для отрисовки текста.


Название: Re: Определить QRect для слова в QLabel
Отправлено: Igors от Сентябрь 12, 2009, 12:41
А если просто создать "сверху" нужные QLineEdit'ы и замаскировать их под static text ?


Название: Re: Определить QRect для слова в QLabel
Отправлено: Alp от Сентябрь 12, 2009, 12:59
QLineEdit использует публичный класс QTextLine. А именно метод:
int QTextLine::xToCursor ( qreal x, CursorPosition cpos = CursorBetweenCharacters ) const

Возможно даже, что все уже есть. Остается только это применить, в документации класс описан. И где-то я в QQ видел применение этого класса для отрисовки текста.
Пришлось отказаться от столь подходящего QLineEdit, т.к. в легенде (отображаемом тексте с хотспотами) могут быть символы перевода строки. Методы переводящие точку в позицию курсора и наоборот уже нашел.


Название: Re: Определить QRect для слова в QLabel
Отправлено: Alp от Сентябрь 12, 2009, 13:00
А если просто создать "сверху" нужные QLineEdit'ы и замаскировать их под static text ?
Была мысль. Но что делать если длина строки превышает ширину виджета? Вручную бить на несколько QLineEdit? =)


Название: Re: Определить QRect для слова в QLabel
Отправлено: Alp от Сентябрь 12, 2009, 13:22
К сожалению, проблема решена. Точнее, придется использовать старый алгоритм с разбивкой по словам и подсчетом размеров каждого слова.

Использовать для этих целей QTextEdit и QLineEdit не получится, т.к. метод cursorRect возвращает именно rect, а не геометрию. Т.е. начальная точка курсора всегда будет (0,0).

У QLineEdit cursorRect вообще защищеный, так что при всем желании не взять координаты каретки.

Далее, не совсем понятно как использовать QTextEdit, в отсутствии нормальных программных методов перемещения по тексту. Либо вызывать в цикле moveCursor(NextChar), либо никак. Других методов программно переместить курсор-каретку, похоже, нет. Аналогичные методы у QTextCursor'а не перемещают каретку.


Название: Re: Определить QRect для слова в QLabel
Отправлено: Alp от Сентябрь 23, 2009, 19:45
В результате получилось вот что:

Код
C++ (Qt)
QVector<QRectF> CalcRectsForLines(QRect boundingRect,
                                 const QFontMetrics& fm, const QString& source )
{
   QVector<QRectF> result;
 
   double xPos = 0, yPos = 0;
   int lineCount = 0;
 
   const double lineHeight = fm.height();
   const double spaceWidth = fm.width(" ");
 
   QStringList lineBreakList = source.split("\n");
   foreach(QString line, lineBreakList)
   {
       double leftWidth = 0;
       xPos = 0, yPos = lineCount*lineHeight;
 
       QStringList spacesList = line.split(" ", QString::SkipEmptyParts);
       foreach(QString word, spacesList)
       {
           double wordWidth = fm.width(word);
 
           if( leftWidth + wordWidth >= boundingRect.width() )
           {
               ++lineCount;
               xPos = 0;
               yPos = lineCount*lineHeight;
               leftWidth = 0;
           }
 
           QRectF wordRect = QRectF(xPos, yPos, wordWidth, lineHeight);
           result.push_back(wordRect);
 
           leftWidth += wordWidth + spaceWidth;
           xPos += wordWidth + spaceWidth;
       }
 
       ++lineCount;
   }
 
   double heightDispl = (boundingRect.height() - lineCount*lineHeight)/2;
 
   for( int i = 0; i < result.size(); ++i )
       result[i].moveTop(result[i].y() + heightDispl);
 
   return result;
}
 

На вход получает Rect виджета, на котором рисуется текст (для верного расставления переносов), метрика выбранного шрифта и строку (допускаются символы перевода строки, по умолчанию считается, что wordWrap включен).