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

Тема в разделе "Delphi - Компоненты", создана пользователем -, 24 май 2011.

Статус темы:
Закрыта.
  1. Гость

    Всем доброго дня!
    Пишу свой компонент с использованием GDI+. Компонент предназначен для ввода однострочного текста (типа TEdit), но выводится на экран средствами GDI+. Возникла проблема - при перемещение каретки ввода вправо - влево, каретка появляется не между символами. Для определения ширины символа (и соответственно расстояния на которое должна сдвинуться каретка) использую API функцию GetCharABCWidthsFloat. Функция срабатывает нормально, возвращая три необходимых величины. При этом если увеличить/уменьшить шрифт, изменить гарнитуру и т.п., то и функция начинает возвращать соответственно большие или меньшие данные. Но если перемещать каретку на величину возвращаемую этой функцией она, каретка, появляется где попало.
    Такое впечатление что результат функции не в пикселях, а в относительных единицах, хотя стоит режим MM_TEXT, что исходя из SDK соответствует - одна логическая единица = один пиксель.
    Вот код обработки перемещения каретки
    Код (Delphi):
     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;
    а вот результат работы
    [​IMG]

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

    sinkopa Well-Known Member

    Регистрация:
    17 июн 2009
    Сообщения:
    344
    Симпатии:
    9
    Нет, все-таки тучи на небе мешают проникновению экстрасенсорный флюид... :)
    Придется обратиться к банальной логике... А она подсказывает следующее...
    Уж коли Вы используете функцию SetCaretPos, предполагается у Вас есть некое окно имеющее каретку.
    Так вот.
    1. Если мы обратимся к справке Win32 API то узнаем про функцию SetCaretPos следующее:
    Для того, чтобы определяемые координаты были подчинены режиму отображения контекста устройства, связанного с окном (в Вашем случае я так понимаю это MM_TEXT), это окно должно быть рождено со стилем класса CS_OWNDC.
    Надеюсь что это так (в противном случае SetCaretPos Вас не спасёт) и мы можем ехать дальше.
    2. Использование функции GetCharABCWidthsFloat меня не то чтобы сильно "ужасает"... но согласитесь, логические координаты в которых она работает... float опять же... :)
    При всем при том, что необходимые нам координаты каретки (с учетом вышесказанного конечно) мы всегда можем узнать послав окну сообщение EM_POSFROMCHAR.
    Не могу знать параметры с которыми рождается Ваше окно (это все тучи блин...), поэтому вот Вам два варианта:
    Код (Delphi):
    function GetCaretPosition1(CaretOwnerWindow: HWND; SelStart: LongWord): TPoint;
    begin
    Result := Point(0,0);
    SendMessage(CaretOwnerWindow, EM_POSFROMCHAR,WPARAM(@Result), SelStart);
    end;
    и
    Код (Delphi):
    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, то что надо. хотя... Попробуйте оба.
    Однако, все может оказаться совсем не так... но что поделаешь... тучи блин... :)
     
  3. Гость

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

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

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

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

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

    P.S.
    Если кому нужны подробности - пишите.
     
Загрузка...
Статус темы:
Закрыта.

Поделиться этой страницей