• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

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

    Запись на курс до 25 апреля. Получить промодоступ ...

Компонент ввода текста

  • Автор темы Guest
  • Дата начала
Статус
Закрыто для дальнейших ответов.
G

Guest

Всем доброго дня!
Пишу свой компонент с использованием GDI+. Компонент предназначен для ввода однострочного текста (типа TEdit), но выводится на экран средствами GDI+. Возникла проблема - при перемещение каретки ввода вправо - влево, каретка появляется не между символами. Для определения ширины символа (и соответственно расстояния на которое должна сдвинуться каретка) использую API функцию GetCharABCWidthsFloat. Функция срабатывает нормально, возвращая три необходимых величины. При этом если увеличить/уменьшить шрифт, изменить гарнитуру и т.п., то и функция начинает возвращать соответственно большие или меньшие данные. Но если перемещать каретку на величину возвращаемую этой функцией она, каретка, появляется где попало.
Такое впечатление что результат функции не в пикселях, а в относительных единицах, хотя стоит режим MM_TEXT, что исходя из SDK соответствует - одна логическая единица = один пиксель.
Вот код обработки перемещения каретки
Код:
 if msg.CharCode=VK_LEFT then
begin
GetCharABCWidthsFloat(FDC,ord(caption[FCharPos]),ord(caption[FCharPos]),a);
dec(FCaretPos.X,Round(a.abcfA+a.abcfB+a.abcfC));
dec(FCharPos);
SetCaretPos(FCaretPos.X,FCaretPos.Y);
end else
if msg.CharCode=VK_RIGHT then
begin
GetCharABCWidthsFloat(FDC,integer(pointer(caption[FCharPos+1])),integer(pointer(caption[FCharPos+1])),a);
inc(FCaretPos.X,Round(a.abcfA+a.abcfB+a.abcfC));
inc(FCharPos);
SetCaretPos(FCaretPos.X,FCaretPos.Y);
end;
а вот результат работы


Может кто с подобным уже сталкивался?
Буду благодарен за любые ответы и советы!!!
 
S

sinkopa

Всем доброго дня!
Пишу свой компонент с использованием GDI+. Компонент предназначен для ввода однострочного текста (типа TEdit), но выводится на экран средствами GDI+. Возникла проблема - при перемещение каретки ввода вправо - влево, каретка появляется не между символами.
Может кто с подобным уже сталкивался?
Буду благодарен за любые ответы и советы!!!
Нет, все-таки тучи на небе мешают проникновению экстрасенсорный флюид... :)
Придется обратиться к банальной логике... А она подсказывает следующее...
Уж коли Вы используете функцию SetCaretPos, предполагается у Вас есть некое окно имеющее каретку.
Так вот.
1. Если мы обратимся к справке Win32 API то узнаем про функцию SetCaretPos следующее:
Для того, чтобы определяемые координаты были подчинены режиму отображения контекста устройства, связанного с окном (в Вашем случае я так понимаю это MM_TEXT), это окно должно быть рождено со стилем класса CS_OWNDC.
Надеюсь что это так (в противном случае SetCaretPos Вас не спасёт) и мы можем ехать дальше.
2. Использование функции GetCharABCWidthsFloat меня не то чтобы сильно "ужасает"... но согласитесь, логические координаты в которых она работает... float опять же... :)
При всем при том, что необходимые нам координаты каретки (с учетом вышесказанного конечно) мы всегда можем узнать послав окну сообщение EM_POSFROMCHAR.
Не могу знать параметры с которыми рождается Ваше окно (это все тучи блин...), поэтому вот Вам два варианта:
Код:
function GetCaretPosition1(CaretOwnerWindow: HWND; SelStart: LongWord): TPoint;
begin
Result := Point(0,0);
SendMessage(CaretOwnerWindow, EM_POSFROMCHAR,WPARAM(@Result), SelStart);
end;
и
Код:
function GetCaretPosition2(CaretOwnerWindow: HWND; SelStart: LongWord): TPoint;
var
MsgResult: LongInt;
begin
Result := Point(0,0);
MsgResult := SendMessage(CaretOwnerWindow, EM_POSFROMCHAR, SelStart,0);
if (MsgResult > 0) then
begin
Result.X := LoWord(MsgResult);
Result.Y := HiWord(MsgResult);
end;
end;
CaretOwnerWindow - Handle окна, с контекстом которого Вы работаете
SelStart - Это индекс символа в тексте, после которого Вы хотите поставить курсор каретки (если через SetCaretPos).
Раз Вы говорите "однострочный текст (типа TEdit)", могу предположить что GetCaretPosition2, то что надо. хотя... Попробуйте оба.
Однако, все может оказаться совсем не так... но что поделаешь... тучи блин... :)
 
G

Guest

Наверное я зря не расписал подробнее свой вопрос...
Мой компонент порожден от TWinControl соответственно он просто не получает сообщения типа EM_ он не относится к EDIT контролам. Вывод текста как собственно и его ввод контролирует сам компонент, более того выводится он не на своем DC а на DC родителя (для устранения мерцания компонентов). Отображение текста производится с использованием GDI+, а точнее GdipDrawString.
Поэтому Ваш совет не подходит, но все равно спасибо!

Сегодня попробовал поэкспериментировать с фиксированными шрифтами типа Courier New. Функции типа GetCharABCWidthsFloat, GetTextExtentPoint32, GetCharWidth32 - действительно возвращают постоянную величину на любой символ - но эта величина никак не совпадает с фактической шириной символа в пикселях.

Пытаюсь найти адекватное объяснение...
 
G

Guest

Вопрос решен!
Все оказалось проще чем я думал, впрочем как всегда.
Дело все в том что я выводил текст на экран при помощи GDI+, а именно функции GdipDrawString. Чтоб использовать эту функцию предварительно необходимо создать шрифт используя GdipCreateFont. Вот в этой функции то и была вся загвоздка. Дело в том, что там есть один хитрый параметр UnitPoint (про который я и забыл то совсем по своей собственной глупости). Этот параметр задает интерпретацию логических единиц и пикселей!
Просто у меня строка выводилась, так сказать компактнее чем если бы её выводить стандартными средствами GDI. И получалось что функция GetCharABCWidthsFloat возвращала верные значения, просто моя строка была несколько короче чем нужно.

Можно сказать что тема закрыта...
Всем спасибо.

P.S.
Если кому нужны подробности - пишите.
 
Статус
Закрыто для дальнейших ответов.
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!