всёшохошь

Мелочь, а приятно

Часть 1. Автодополнение

WTF?

Суть автодополнения (autocompletion) заключается в том, что программа при вводе пользователем текста в определенное поле самостоятельно предлагает оставшуюся часть текста.

Первый простейший пример — ввод адреса в адресной строке IE или «Проводника». Только e нажимаешь, а он уже E:\ предлагает.

Второй простейший пример — некоторые поля в iTunes. Оформляете, к примеру, голый mp3: в Artist только набрали A — сразу же A-ha или AC/DC предлагается.

Но автодополнение должно не просто автодополнять: это должно происходить таким образом, чтобы не раздражать и без того нервного пользователя.

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

Приступаем к реализации

Для тестового приложения понадобится поле ввода (назовем его edWord) и многострочное поле (его назовем memWords). В первое поле будет вводиться текст, а во втором — храниться варианты. Варианты можно дописывать в прямом эфире — во время выполнения программы. Рекомендую пять-шесть штук ввести до компиляции, чтобы каждый раз не делать это заново.

Нетерпеливые могут скачать zip-архив (210 КБ) с программой-примером, использующей код из этой статьи.

Итак

Нам нужно постоянно следить за edWord, чтобы в нужный момент дополнить текст. Для этого подойдет событие onChange. Вдохнем в него жизнь:

var
  i, EditPos : integer;
  TypedString, Word : WideString;
begin
  for i := 0 to memWords.Lines.Count - 1 do
  begin
    Word := memWords.Lines [i];

    if WideTextPos (edWord.Text, Word) = 1 then
    begin
      EditPos := edWord.SelStart;
      TypedString := edWord.Text;
      edWord.Text := Word;

      edWord.SelStart := EditPos;
      edWord.SelLength := length (Word) - length (TypedString);

      break;
    end;
  end;
end;

Разберемся, что здесь происходит. Поочередно перебирая варианты, смотрим, совпадает ли введенный текст с началом варианта (середина и конец не катят). Если совпадает, текст замещается вариантом и выделяется все кроме введенной пользователем части текста. Цикл перебора останавливается, всем спасибо.

Но в таком виде автодополнение еще сыровато: выделенный текст нельзя удалить; также имеет место неадекватная реакция на вырезание, вставку и отмену (вот такой он шухерной, этот onChange).

В общем, нужно определить, когда нужно автодополнять, а когда нет. Запускаем iTunes, играемся с редактированием, запоминаем поведение автодополнения. Переводим мысли в текст.

Дополнять текст не нужно после нажатия Backspace или Delete, после отмены (Ctrl+Z), вырезания (Ctrl+X) и вставки (Ctrl+V) текста.

В качестве флажка для определения потребности в дополнении можно объявить дополнительную переменную, а можно воспользоваться ярлычком — свойством Tag (тип integer), которое есть у каждого потомка TComponent. Пусть Tag будет равняться нулю при необходимости дополнения и единице в противном случае.

Отлавливать нажатие вышеперечисленных клавиш в edWord будем событием onKeyDown:

if (Key = VK_BACK) or (Key = VK_DELETE)
 or ((ssCtrl in Shift) and ((Key = ord ('Z')) or (Key = ord ('X')) or (Key = ord ('V'))))
then
  edWord.Tag := 1
else
  edWord.Tag := 0;

Допишем чтение ярлычка. В начало обработки onChange поместим фейс-контроль:

if edWord.Tag = 1 then exit;

И перед прерыванием (break) цикла проверки:

edWord.Tag := 1;

Теперь наше автодополнение работает правильно и вежливо. Осталось только придумать, где его можно применить. Наиболее очевидное использование — в программах, работающих с множеством записей одинакового вида (например, iTunes под это описание подходит).