вторник, 12 января 2021 г.

Создаем системный хинт

Начиная с Delphi 2009 у компонента TEdit появилось замечательное булевое свойство "NumbersOnly". Его роль понятна из названия - ограничения вводимых символов (только цифры). При попытке ввести иной символ всплывает системный хинт:


Данный хинт является системным, т.е. генерируется самой ОС Windows. Если мы хотим повесить такой хинт на определенные события, придётся создавать его "вручную". Т.е. с помощью API-функций для работы с окнами.

Создадим три процедуры: для создания хинта, для его показа и для сокрытия/удаления:

const
  TOOLTIPS_CLASS    = 'tooltips_class32';

  TTS_ALWAYSTIP     = $01;
  TTS_NOPREFIX      = $02;
  TTS_BALLOON       = $40;

  TTF_SUBCLASS      = $0010;
  TTF_TRACK         = $0020;
  TTF_TRANSPARENT   = $0100;
  TTF_CENTERTIP     = $0002;

  TTM_ACTIVATE      = WM_USER + 1;
  TTM_ADDTOOL       = WM_USER + 50;
  TTM_TRACKACTIVATE = WM_USER + 17;
  TTM_TRACKPOSITION = WM_USER + 18;
  TTM_SETTITLE      = WM_USER + 32;

var
  ToolTipHandle: Cardinal;
  ToolInfo: TToolInfo;

procedure CreateToolTips();
var
  vRect: TRect;

begin
  ToolTipHandle := CreateWindowEx(WS_EX_TOPMOST,
                                  TOOLTIPS_CLASS,
                                  nil,
                                  WS_POPUP or TTS_NOPREFIX or TTS_BALLOON or TTS_ALWAYSTIP,
                                  Integer(CW_USEDEFAULT),
                                  Integer(CW_USEDEFAULT),
                                  Integer(CW_USEDEFAULT),
                                  Integer(CW_USEDEFAULT),
                                  Application.Handle,
                                  0,
                                  Application.Handle,
                                  nil);

  GetWindowRect(Application.Handle, &vRect);
end;

procedure AddToolTip(Sender: TObject; IconType: Integer; Title: AnsiString; Text: PWideChar);
var
  vControl: TWinControl;
  vCaretPos: TPoint;
  X, Y: Integer;

begin
  vControl := Sender as TWinControl;

  if ToolTipHandle = 0 then
    exit();

  SetWindowPos(ToolTipHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE);

  ToolInfo.cbSize := SizeOf(TToolInfo);
  ToolInfo.uFlags := {TTF_CENTERTIP or} TTF_TRACK or TTF_TRANSPARENT or TTF_SUBCLASS;
  ToolInfo.hwnd   := vControl.Handle;
  ToolInfo.hInst  := Application.Handle;

  GetClientRect(ToolInfo.hwnd, ToolInfo.Rect);
  ToolInfo.lpszText := Text;

  GetCaretPos(vCaretPos);
  X := vControl.ClientOrigin.X + vCaretPos.X;
  Y := vControl.ClientOrigin.Y + vControl.ClientHeight - vCaretPos.Y;

  SendMessage(ToolTipHandle, TTM_ADDTOOL,       WPARAM(1), LPARAM(@ToolInfo));
  SendMessage(ToolTipHandle, TTM_SETTITLE,      IconType,  LPARAM(PWideChar(Title)));
  SendMessage(ToolTipHandle, TTM_TRACKPOSITION, WPARAM(0), LPARAM(MakeLong(X, Y)));
  SendMessage(ToolTipHandle, TTM_TRACKACTIVATE, WPARAM(1), LPARAM(@ToolInfo));
end;

procedure HideToolTip();
begin
  SendMessage(ToolTipHandle, TTM_TRACKACTIVATE, WPARAM(0), LPARAM(@ToolInfo));
  SendMessage(ToolTipHandle, TTM_DELTOOL,       WPARAM(0), LPARAM(@ToolInfo));
end;

При создании формы создаем наш хинт, показывать его будем в событии OnKeyPress у TEdit'а, ну а закрывать по таймеру

procedure TForm1.edtLogKeyPress(Sender: TObject; var Key: Char);
begin
  if not charinset(Key,['0'..'9', ',', #8]) then
  begin
    MessageBeep(MB_ICONWARNING);
    AddToolTip(Sender, 3, 'Недопустимый символ', 'Разрешены только цифры и запятая');
    Timer.Enabled := True;
    Key := #0;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateToolTips();
end;

procedure TForm1.TimerTimer(Sender: TObject);
begin
  HideToolTip();
  Timer.Enabled := False;
end;
Получаемый результат:

понедельник, 21 декабря 2020 г.

Про частично определенные модули

 Не всегда к имени модуля бывает добавлен префикс - имя области действия этого модуля. Я стараюсь писать программы так, чтобы все модули всегда включали такой префикс. Но как быть со сторонними модулями?

Рассмотрим случай, когда требуется использовать библиотеку OExport в консольном проекте (Embarcadero RAD Studio 10.3.3). Для этого в тестовом примере консольного приложения я подключил модули OExport.pas и OExport_VCL.pas (также я добавил модуль MemTableEh.pas библиотеки EhLib).

Код тестового примера:

При компиляции получаю ошибку, связанную с неразрешенностью компилятором частично определенных имен модулей в OExport_VCL:
Компилятор не понимает к чему относится модуль Graphics - к Vcl.Graphics или FMX.Graphics (аналогичная ошибка, если закомментировать OExport_VCL в секции uses, будет с модулем Controls в модуле DBGridEh.pas).

Для того, чтоб решить эту проблему, мне пришлось вручную править файл .dproj.
Откроем файл в текстовом редакторе. В самом начале находим узел "FrameworkType".

Необходимо заменить значение с "None" на "VCL"
Чуть ниже найдем "PropertyGroup Condition="'$(Base)'!=''", а внутри него узел "DCC_Namespace"
Добавим в его значение следующие строчки: "Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples; Vcl.Shell"
Готово! Теперь при компиляции ошибок не будет.





воскресенье, 29 ноября 2020 г.

Не работает отладчик для x64

Отлаживая на работе проект под x64 (Embarcadero Delphi 10.3.3), заметил, что точки останова становятся не активны после запуска проекта под дебаггером.

Выглядит это вот так (на тестовом примере):

Спустя N-ое количество времени проблема была найдена и устранена. Оказывается, если в полном пути до проекта есть русские буквы, то отладчик для x64 не работает (для x32 всё тип-топ). Соответственно проблема была решена переименованием директорий из русских букв в английские.Вот такой вот бажок!

пятница, 18 сентября 2020 г.

Установка IP*Works

На днях возникла необходимость подключиться к рабочему IMAP-серверу. Почитав отзывы в Интернете, я остановился на двух вариантах работы с IMAP в Delphi:

1. TIdIMAP4. Компонент Indy.

2. TipwIMAP из библиотеки IP*Works.

Первый вариант не подошел, ибо 1) ранее в  Инди часто бывали утечки, 2) Инди тащат за собой VCL.

Скачав с одного известного сайта установщик IP*Works и запустив его, Я заметил, что на шаге выбора студии нет моей версии. Я использую Delphi 10.3 Rio, а в списке предложенных вариантов самая последняя версия была 10.1 Berlin. Её и выбрал.

После установки в папке C:\Program Files\nsoftware\IPWorks V9 Delphi Edition (далее "папка IP*Works") появляется всё необходимо для работы с компонентами - каталоги \pas и \lib (кстати, библиотеки находятся в \pas и имеют разрешение .dru). Не было только пакета bpl. Можно, конечно, работать с невизуальными компонентами и так, но всё-таки хочется, чтобы они были в палитре.

А это значит, что придётся ставить компоненты вручную. Далее идёт инструкция, как это сделать. 

1. Запустить Delphi 10.3 Rio, выбрать "File" → "New"→ "Package Delphi"

2. Далее создать в папке IP*Works папку project, сохранить туда проект пакета под именем "IPWorks"

3. В дереве проекта ПКМ по "Contains" → "Add...", выбрать все pas'овские файлы из папки \pas

4. В дереве проекта ПКМ по "IPWorks.bpl" → Build → Install. Получить сообщение, что компоненты установлены успешно

5. Выбрать в верхнем меню Tools → Options → Language → Library → (Selected platform="Windows 32-bit")

Добавить в Library path
C:\Program Files\nsoftware\IPWorks V9 Delphi Edition\lib
C:\Program Files\nsoftware\IPWorks V9 Delphi Edition\pas

Добавить в Browsing path
C:\Program Files\nsoftware\IPWorks V9 Delphi Edition\pas

6. Настройка завершена! Теперь можно добавлять невизуальные компоненты на форму из палитры.

среда, 22 июня 2016 г.

Великое начало

Ну-с, попробуем... Желание завести блог возникло давно, но всё не было времени. МЕ всем советует вести дневник, это обязательно для самоработы.