:: MVP ::
|
|
:: RSS ::
|
|
|
TouchKeyboard - компонент виртуальной клавиатуры, имитирующий традиционную клавиатуру компьютера.
В своем комплекте она имеет несколько макетов - цифровую клавиатуру и несколько клавиатур со стандартной
раскладкой для целого ряда языков ввода (от 101 клавиши в американской раскладке до 106 клавиш в японской).
Достаточно удобное решение для программ, которые планируется эксплуатировать на планшете.
В процессе работы с этой клавиатурой обнаружился весьма существенный недостаток - она не позволяет
переключаться между клавиатурными раскладками, сколько не долби по клавишам Ctrl +
Shift (или Alt + Shift, в зависимости от настройки вашей системы).
Такой бедой не страдает ни экранная клавиатура (osk.exe), в которой эти сочетания клавиш прекрасно
отрабатывают, ни сенсорная (tabtip.exe), в которой для переключения между языками предусмотрена
специальная кнопка.
Надо ли говорить о том, что отсутствие возможности переключения языков способно доставить пользователям весьма
ощутимые неудобства. Попробуем решить эту проблему, и для начала разберемся, что представляет собой TouchKeyboard.
Макеты (раскладки клавиатуры) - это обычные ресурсы. Чтобы подключить их к своему приложению,
достаточно добавить в uses модуль Vcl.Touch.Keyboard.
Посмотрим, какие макеты мы имеем в своем распоряжении, для чего напишем небольшую вспомогательную утилиту
(ее полный исходный код находится в каталоге HelperTool в архиве с примерами к данной статье).
type
PEnumData = ^TEnumData;
TEnumData = record
Lines: TStrings;
end;
function EnumResNames(Module: HMODULE; ResType, ResName: PChar;
LParam: NativeInt): BOOL; stdcall;
const
sResourceName = 'KEYBOARD';
begin
if Pos(sResourceName, ResName) > 0 then
PEnumData(LParam)^.Lines.Add(ResName);
Result := True;
end;
procedure TKeyboardTranslator.GetKeyboardLayoutNames;
var
EnumData: PEnumData;
begin
try
New(EnumData);
EnumData^.Lines := FLayoutNames;
try
EnumResourceNames(HInstance, RT_RCDATA, @EnumResNames, LParam(EnumData));
except
FLayoutNames.Clear;
end;
finally
Dispose(EnumData);
end;
end;
|
После запуска утилиты мы должны увидеть такой список:
Чтобы было проще разбираться с тем, что представляет собой конкретный макет, сериализуем его в формат
XML. Для этого выберем требуемый макет из списка и нажмем на кнопку "Выгрузить в XML" (при этом
выгружаемый файл сформируется в одном каталоге с утилитой).
procedure TKeyboardTranslator.SaveToXml(const FileName, LayoutName: string);
function LoadLayout(const LayoutName: string): TVirtualKeyLayout;
begin
{...}
end;
function ComboKeysToString(const Keys: TKeyDataArray): string;
begin
{...}
end;
procedure MakeXmlDocument(var Document: IXMLDocument; KeyboardLayout: TVirtualKeyLayout);
begin
{...}
end;
var
Document: IXMLDocument;
Layout: TVirtualKeyLayout;
begin
if (Trim(FileName) <> '') and (Trim(LayoutName) <> '') then
begin
Layout := LoadLayout(LayoutName);
if Assigned(Layout) then
begin
Document := TXMLDocument.Create(nil);
if Document = nil then
Exit;
Document.NodeIndentStr := #9;
Document.Options:= Document.Options + [doNodeAutoIndent];
Document.Active := True;
MakeXmlDocument(Document, Layout);
Document.SaveToFile(FileName);
end;
end;
end;
|
Здесь мы загружаем из ресурса нужный макет (представляющий собой ни что иное как экземпляр класса TVirtualKeyLayout),
и пробегаясь по его свойствам формируем XML файл. Код вспомогательных методов я намеренно не привожу из-за его достаточно
больших размеров, это помешает нам сконцентрироваться на сути.
Вот результат сериализации ресурса STANDARD101KEYBOARD из Delphi 10.2.2 (в других версиях результат может, и скорее
всего будет, немного отличаться):
<Keyboard KeyboardName="Standard101" KeyboardType="Standard" Width="848" Height="262" MinWidth="550"
MinHeight="180" RowHeight="48">
<Language LanguageName="az" Name="Azeri"/>
<Language LanguageName="be" Name="Belarusian"/>
<Language LanguageName="en" Name="English"/>
<Language LanguageName="es" Name="Spanish"/>
<Language LanguageName="hy" Name="Armenian"/>
<Language LanguageName="ka" Name="Georgian"/>
<Language LanguageName="nl-BE" Name="Dutch (Belgium)"/>
<Language LanguageName="ru" Name="Russian"/>
<Language LanguageName="sr" Name="Serbian"/>
<Language LanguageName="lt" Name="Lithuainian"/>
<Row TopMargin="0" BottomMargin="2">
<Key Caption="Esc" ScanCode="1" Width="64" Height="48" RightMargin="2" Stretch="True"
PublishedName="Esc"/>
<Key ScanCode="41" Width="48" Height="48" LeftMargin="2" RightMargin="2"/>
<Key ScanCode="2" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="59" ModifierName="fn" FontSize="10" Caption="F1"/>
<Modifier ScanCode="2" ModifierName="caps" LanguageName="lt"/>
</Key>
<Key ScanCode="3" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="60" ModifierName="fn" FontSize="10" Caption="F2"/>
<Modifier ScanCode="3" ModifierName="caps" LanguageName="hy"/>
<Modifier ScanCode="3" ModifierName="caps" LanguageName="lt"/>
</Key>
<Key ScanCode="4" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="61" ModifierName="fn" FontSize="10" Caption="F3"/>
<Modifier ScanCode="4" ModifierName="caps" LanguageName="hy"/>
<Modifier ScanCode="4" ModifierName="caps" LanguageName="lt"/>
</Key>
<Key ScanCode="5" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="62" ModifierName="fn" FontSize="10" Caption="F4"/>
<Modifier ScanCode="5" ModifierName="caps" LanguageName="lt"/>
</Key>
<Key ScanCode="6" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="63" ModifierName="fn" FontSize="10" Caption="F5"/>
<Modifier ScanCode="6" ModifierName="caps" LanguageName="lt"/>
</Key>
<Key ScanCode="7" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="64" ModifierName="fn" FontSize="10" Caption="F6"/>
<Modifier ScanCode="7" ModifierName="caps" LanguageName="lt"/>
</Key>
<Key ScanCode="8" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="65" ModifierName="fn" FontSize="10" Caption="F7"/>
<Modifier ScanCode="8" ModifierName="caps" LanguageName="lt"/>
</Key>
<Key ScanCode="9" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="66" ModifierName="fn" FontSize="10" Caption="F8"/>
<Modifier ScanCode="9" ModifierName="caps" LanguageName="lt"/>
</Key>
<Key ScanCode="10" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="67" ModifierName="fn" FontSize="10" Caption="F9"/>
</Key>
<Key ScanCode="11" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="68" ModifierName="fn" FontSize="10" Caption="F10"/>
<Modifier ScanCode="11" ModifierName="caps" LanguageName="hy"/>
</Key>
<Key ScanCode="12" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="69" ModifierName="fn" FontSize="10" Caption="F11"/>
<Modifier ScanCode="12" ModifierName="caps" LanguageName="hy"/>
</Key>
<Key ScanCode="13" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="70" ModifierName="fn" FontSize="10" Caption="F12"/>
<Modifier ScanCode="13" ModifierName="caps" LanguageName="hy"/>
<Modifier ScanCode="13" ModifierName="caps" LanguageName="lt"/>
</Key>
<Key KeyImage="3" ScanCode="14" Width="78" Height="48" LeftMargin="2" Stretch="True"
PublishedName="Backspace"/>
</Row>
<Row TopMargin="2" BottomMargin="2">
<Key KeyImage="0" ScanCode="15" Width="85" Height="48" RightMargin="2" Stretch="True"
PublishedName="Tab"/>
<Key ScanCode="16" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="16" ModifierName="caps"/>
</Key>
<Key ScanCode="17" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="17" ModifierName="caps"/>
</Key>
<Key ScanCode="18" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="18" ModifierName="caps"/>
</Key>
<Key ScanCode="19" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="19" ModifierName="caps"/>
</Key>
<Key ScanCode="20" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="20" ModifierName="caps"/>
</Key>
<Key ScanCode="21" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="21" ModifierName="caps"/>
</Key>
<Key ScanCode="22" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="22" ModifierName="caps"/>
</Key>
<Key ScanCode="23" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="23" ModifierName="caps"/>
</Key>
<Key ScanCode="24" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="24" ModifierName="caps"/>
</Key>
<Key ScanCode="25" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="25" ModifierName="caps"/>
</Key>
<Key ScanCode="26" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="26" ModifierName="caps" LanguageName="hy"/>
</Key>
<Key ScanCode="27" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="27" ModifierName="caps" LanguageName="hy"/>
</Key>
<Key ScanCode="43" Width="48" Height="48" LeftMargin="2" RightMargin="2"/>
<Key Caption="Del" ScanCode="83" Width="57" Height="48" LeftMargin="2" Stretch="True"
PublishedName="Del">
<Language LanguageName="es" Caption="Supr"/>
</Key>
</Row>
<Row TopMargin="2" BottomMargin="2">
<Key Caption="Caps" ScanCode="58" Width="115" Height="48" RightMargin="2" Toggle="True"
Stretch="True" ModifierName="caps" PublishedName="Caps">
<Language LanguageName="es" Caption="Bloq May"/>
</Key>
<Key ScanCode="30" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="30" ModifierName="caps"/>
</Key>
<Key ScanCode="31" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="31" ModifierName="caps"/>
</Key>
<Key ScanCode="32" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="32" ModifierName="caps"/>
</Key>
<Key ScanCode="33" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="33" ModifierName="caps"/>
</Key>
<Key ScanCode="34" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="34" ModifierName="caps"/>
</Key>
<Key ScanCode="35" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="35" ModifierName="caps"/>
</Key>
<Key ScanCode="36" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="36" ModifierName="caps"/>
</Key>
<Key ScanCode="37" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="37" ModifierName="caps"/>
</Key>
<Key ScanCode="38" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="38" ModifierName="caps"/>
</Key>
<Key ScanCode="39" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="39" ModifierName="caps" LanguageName="es"/>
<Modifier ScanCode="39" ModifierName="caps" LanguageName="hy"/>
</Key>
<Key ScanCode="40" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="40" ModifierName="caps" LanguageName="hy"/>
</Key>
<Key KeyImage="2" ScanCode="28" Width="123" Height="48" LeftMargin="2" Stretch="True"
PublishedName="Enter"/>
</Row>
<Row TopMargin="2" BottomMargin="2">
<Key KeyImage="1" ScanCode="42" Width="125" Height="48" RightMargin="2" Stretch="True"
ModifierName="shift" PublishedName="LeftShift"/>
<Key ScanCode="44" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="44" ModifierName="caps"/>
</Key>
<Key ScanCode="45" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="45" ModifierName="caps"/>
</Key>
<Key ScanCode="46" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="46" ModifierName="caps"/>
</Key>
<Key ScanCode="47" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="47" ModifierName="caps"/>
</Key>
<Key ScanCode="48" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="48" ModifierName="caps"/>
</Key>
<Key ScanCode="49" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="49" ModifierName="caps"/>
</Key>
<Key ScanCode="50" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="50" ModifierName="caps"/>
</Key>
<Key ScanCode="51" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="51" ModifierName="caps" LanguageName="hy"/>
</Key>
<Key ScanCode="52" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="52" ModifierName="caps" LanguageName="hy"/>
</Key>
<Key ScanCode="53" Width="48" Height="48" LeftMargin="2" RightMargin="2">
<Modifier ScanCode="53" ModifierName="caps" LanguageName="hy"/>
</Key>
<Key KeyImage="1" ScanCode="42" Width="65" Height="48" LeftMargin="2" RightMargin="2"
Stretch="True" ModifierName="shift" PublishedName="RightShift"/>
<Key KeyImage="4" ScanCode="72" Width="48" Height="48" LeftMargin="2" RightMargin="2"/>
<Key Caption="Fn" ScanCode="-2" Width="48" Height="48" LeftMargin="2" Toggle="True"
ModifierName="fn"/>
</Row>
<Row TopMargin="2" BottomMargin="0">
<Key Caption="Ctrl" ScanCode="29" Width="68" Height="48" RightMargin="2" ModifierName="ctrl"
PublishedName="LeftCtrl"/>
<Key Caption="Alt" ScanCode="56" Width="82" Height="48" LeftMargin="2" RightMargin="2" ModifierName="alt"
PublishedName="LeftAlt"/>
<Key ScanCode="57" Width="321" Height="48" LeftMargin="2" RightMargin="2" Stretch="True"/>
<Key Caption="Alt" ScanCode="56" Width="82" Height="48" LeftMargin="2" RightMargin="2" ModifierName="alt"
PublishedName="RightAlt" ComboKeys="vk=17;sc=29;vk=18;sc=56">
<Language LanguageName="ka" Caption="AltGr"/>
<Language LanguageName="sr" Caption="AltGr"/>
<Language LanguageName="es" Caption="AltGr"/>
</Key>
<Key Caption="Ctrl" ScanCode="29" Width="69" Height="48" LeftMargin="2" RightMargin="2" ModifierName="ctrl"
PublishedName="RightCtrl"/>
<Key KeyImage="6" ScanCode="75" Width="48" Height="48" LeftMargin="2" RightMargin="2"/>
<Key KeyImage="5" ScanCode="80" Width="48" Height="48" LeftMargin="2" RightMargin="2"/>
<Key KeyImage="7" ScanCode="77" Width="48" Height="48" LeftMargin="2"/>
</Row>
</Keyboard>
|
Первое, что мы видим - языки, поддерживаемые данным макетом. Далее идут ряды клавиш в тегах <Row>..</Row>.
Это наводит на мысль о том, что мы можем добавить в макет свою кнопку для переключения языков, аналогично сенсорной
клавиатуре (tabtip.exe). Мне показалось удобным поместить эту клавишу в нижний ряд после правой клавиши Ctrl.
Данная кнопка должна отражать в заголовке раскладку для выбранного в данный момент языка в системе. К счастью
в TouchKeyboard такой механизм уже предусмотрен, обратите внимание на строчки для правой кнопки Alt:
<Key Caption="Alt" ScanCode="56" Width="82" Height="48" LeftMargin="2" RightMargin="2" ModifierName="alt"
PublishedName="RightAlt" ComboKeys="vk=17;sc=29;vk=18;sc=56">
<Language LanguageName="ka" Caption="AltGr"/>
<Language LanguageName="sr" Caption="AltGr"/>
<Language LanguageName="es" Caption="AltGr"/>
</Key>
|
Осталось определить соответствие между тегом языка (например, ru-RU) и заголовком для кнопки к нему. Подробно останавливаться
на этом здесь я не буду, кому интересно, прочтите статью "Управление языками
и раскладками в Windows". Все найденные соответствия (возможно на вашей машине список будет отличаться от того, что получил
я) вставляем в XML файл (проделаем это со всеми тремя макетами):
<Row TopMargin="2" BottomMargin="0">
<Key Caption="Ctrl" ScanCode="29" Width="68" Height="48" RightMargin="2" ModifierName="ctrl"
PublishedName="LeftCtrl"/>
<Key Caption="Alt" ScanCode="56" Width="82" Height="48" LeftMargin="2" RightMargin="2" ModifierName="alt"
PublishedName="LeftAlt"/>
<Key ScanCode="57" Width="321" Height="48" LeftMargin="2" RightMargin="2" Stretch="True"/>
<Key Caption="Alt" ScanCode="56" Width="82" Height="48" LeftMargin="2" RightMargin="2" ModifierName="alt"
PublishedName="RightAlt" ComboKeys="vk=17;sc=29;vk=18;sc=56">
<Language LanguageName="ka" Caption="AltGr"/>
<Language LanguageName="sr" Caption="AltGr"/>
<Language LanguageName="es" Caption="AltGr"/>
</Key>
<Key Caption="Ctrl" ScanCode="29" Width="69" Height="48" LeftMargin="2" RightMargin="2" ModifierName="ctrl"
PublishedName="RightCtrl"/>
<Key ScanCode="-255" ComboKeys="vk=-255;sc=-255" Width="65" Height="48" LeftMargin="2" RightMargin="2">
<Language LanguageName="iu-Cans-CA" Caption="IU"/>
<Language LanguageName="bs-Cyrl-BA" Caption="BS"/>
<Language LanguageName="en-IE" Caption="EN"/>
<Language LanguageName="zh-MO" Caption="ZH"/>
<Language LanguageName="fr-CH" Caption="FR"/>
<Language LanguageName="en-CA" Caption="EN"/>
<Language LanguageName="zh-SG" Caption="ZH"/>
<Language LanguageName="sr-Cyrl-CS" Caption="SR"/>
<Language LanguageName="fr-CA" Caption="FR"/>
<Language LanguageName="zh-HK" Caption="ZH"/>
<Language LanguageName="iu-Latn-CA" Caption="IU"/>
<Language LanguageName="mn-Mong-CN" Caption="MN"/>
<Language LanguageName="uz-Cyrl-UZ" Caption="UZ"/>
<Language LanguageName="se-SE" Caption="SM"/>
<Language LanguageName="az-Cyrl-AZ" Caption="AZ"/>
<Language LanguageName="sr-Latn-CS" Caption="SR"/>
<Language LanguageName="pt-PT" Caption="PT"/>
<Language LanguageName="nl-BE" Caption="NL"/>
<Language LanguageName="fr-BE" Caption="FR"/>
<Language LanguageName="es-MX" Caption="ES"/>
<Language LanguageName="en-GB" Caption="EN"/>
<Language LanguageName="de-CH" Caption="DE"/>
<Language LanguageName="zh-CN" Caption="CH"/>
<Language LanguageName="wo-SN" Caption="WO"/>
<Language LanguageName="sah-RU" Caption="SA"/>
<Language LanguageName="mi-NZ" Caption="MR"/>
<Language LanguageName="ug-CN" Caption="UI"/>
<Language LanguageName="ig-NG" Caption="IB"/>
<Language LanguageName="kl-GL" Caption="KA"/>
<Language LanguageName="lb-LU" Caption="LB"/>
<Language LanguageName="ba-RU" Caption="BA"/>
<Language LanguageName="nso-ZA" Caption="NS"/>
<Language LanguageName="yo-NG" Caption="YO"/>
<Language LanguageName="ha-Latn-NG" Caption="HA"/>
<Language LanguageName="dv-MV" Caption="DI"/>
<Language LanguageName="ps-AF" Caption="PA"/>
<Language LanguageName="ne-NP" Caption="NE"/>
<Language LanguageName="si-LK" Caption="SI"/>
<Language LanguageName="syr-SY" Caption="SY"/>
<Language LanguageName="lo-LA" Caption="LA"/>
<Language LanguageName="km-KH" Caption="KH"/>
<Language LanguageName="cy-GB" Caption="CY"/>
<Language LanguageName="bo-CN" Caption="BO"/>
<Language LanguageName="mn-MN" Caption="MN"/>
<Language LanguageName="mr-IN" Caption="MA"/>
<Language LanguageName="as-IN" Caption="AS"/>
<Language LanguageName="ml-IN" Caption="MY"/>
<Language LanguageName="kn-IN" Caption="KD"/>
<Language LanguageName="te-IN" Caption="TE"/>
<Language LanguageName="ta-IN" Caption="TA"/>
<Language LanguageName="or-IN" Caption="OR"/>
<Language LanguageName="gu-IN" Caption="GU"/>
<Language LanguageName="pa-IN" Caption="PA"/>
<Language LanguageName="bn-IN" Caption="BN"/>
<Language LanguageName="tt-RU" Caption="TT"/>
<Language LanguageName="tk-TM" Caption="TU"/>
<Language LanguageName="ky-KG" Caption="KY"/>
<Language LanguageName="kk-KZ" Caption="KK"/>
<Language LanguageName="se-NO" Caption="SM"/>
<Language LanguageName="mt-MT" Caption="ML"/>
<Language LanguageName="hi-IN" Caption="HI"/>
<Language LanguageName="fo-FO" Caption="FO"/>
<Language LanguageName="ka-GE" Caption="KA"/>
<Language LanguageName="tn-ZA" Caption="TS"/>
<Language LanguageName="mk-MK" Caption="MK"/>
<Language LanguageName="hsb-DE" Caption="HS"/>
<Language LanguageName="az-Latn-AZ" Caption="AZ"/>
<Language LanguageName="hy-AM" Caption="HY"/>
<Language LanguageName="vi-VN" Caption="VI"/>
<Language LanguageName="fa-IR" Caption="FA"/>
<Language LanguageName="tg-Cyrl-TJ" Caption="TA"/>
<Language LanguageName="lt-LT" Caption="LT"/>
<Language LanguageName="lv-LV" Caption="LV"/>
<Language LanguageName="et-EE" Caption="ET"/>
<Language LanguageName="sl-SI" Caption="SL"/>
<Language LanguageName="be-BY" Caption="BE"/>
<Language LanguageName="uk-UA" Caption="UK"/>
<Language LanguageName="ur-PK" Caption="UR"/>
<Language LanguageName="tr-TR" Caption="TR"/>
<Language LanguageName="th-TH" Caption="TH"/>
<Language LanguageName="sv-SE" Caption="SV"/>
<Language LanguageName="sq-AL" Caption="SQ"/>
<Language LanguageName="sk-SK" Caption="SK"/>
<Language LanguageName="hr-HR" Caption="HR"/>
<Language LanguageName="ru-RU" Caption="RU"/>
<Language LanguageName="ro-RO" Caption="RO"/>
<Language LanguageName="pt-BR" Caption="PT"/>
<Language LanguageName="pl-PL" Caption="PL"/>
<Language LanguageName="nb-NO" Caption="NO"/>
<Language LanguageName="nl-NL" Caption="NL"/>
<Language LanguageName="ko-KR" Caption="KO"/>
<Language LanguageName="ja-JP" Caption="JP"/>
<Language LanguageName="it-IT" Caption="IT"/>
<Language LanguageName="is-IS" Caption="IS"/>
<Language LanguageName="hu-HU" Caption="HU"/>
<Language LanguageName="he-IL" Caption="HE"/>
<Language LanguageName="fr-FR" Caption="FR"/>
<Language LanguageName="fi-FI" Caption="FI"/>
<Language LanguageName="es-ES_tradnl" Caption="ES"/>
<Language LanguageName="en-US" Caption="EN"/>
<Language LanguageName="el-GR" Caption="EL"/>
<Language LanguageName="de-DE" Caption="DE"/>
<Language LanguageName="da-DK" Caption="DA"/>
<Language LanguageName="cs-CZ" Caption="CS"/>
<Language LanguageName="zh-TW" Caption="CH"/>
<Language LanguageName="bg-BG" Caption="BG"/>
<Language LanguageName="ar-SA" Caption="AR"/>
</Key>
<Key KeyImage="6" ScanCode="75" Width="48" Height="48" LeftMargin="2" RightMargin="2"/>
<Key KeyImage="5" ScanCode="80" Width="48" Height="48" LeftMargin="2" RightMargin="2"/>
<Key KeyImage="7" ScanCode="77" Width="48" Height="48" LeftMargin="2"/>
</Row>
|
Конечно, с точки зрения перфикционизма, было бы правильнее добавлять в каждый макет только те языки, которые
реально им поддерживаются - это позволило бы сократить размер получившегося в итоге ресурса. Но, так как экономия
в размере весьма не существенна по сравнению со временем, затраченным на анализ и выборку нужных языков, то в
рамках данной статьи я не буду этого делать. (Примеры модифицированных ресурсов можно найти в каталоге
ModifyedResource в архиве с примерами к данной статье).
Осталось десериализовать наш модифицированный XML файл (напомню, он должен лежать в одном каталоге
с утилитой), для чего нажмем на кнопку "Скомпилировать", предварительно выбрав нужный ресурс в списке.
function TKeyboardTranslator.Compile(const FileName: string;
const OutFileName: string = ''): Boolean;
var
TmpFileName: string;
KeyboardLayout: TVirtualKeyLayout;
Stream: TFileStream;
Temp: TVirtualKeyLayout;
begin
Result := False;
if FileExists(FileName) then
begin
KeyboardLayout := LoadXmlFile(FileName);
if OutFileName = '' then
TmpFileName := ChangeFileExt(FileName, '.res')
else
begin
TmpFileName := OutFileName;
if LowerCase(ExtractFileExt(TmpFileName)) <> '.res' then
TmpFileName := TmpFileName + '.res';
end;
if FileExists(TmpFileName) then
Stream := TFileStream.Create(TmpFileName, fmOpenWrite)
else
Stream := TFileStream.Create(TmpFileName, fmCreate);
try
KeyboardLayout.SaveToStream(Stream);
Result := True;
finally
Stream.Free;
end;
end;
end;
|
Скомпилированный ресурс готов и находится рядом с XML файлом. Уже сейчас, в качестве эксперимента, можно с
помощью какого-нибудь редактора ресурсов подменить в тестовом приложении модифицированные ресурсы. Запустив
модифицированное приложение мы увидим нашу кнопку, которая корректно отображает выбранный в системе язык, и
корректно ведет себя при переключении раскладки.
Нам же нужно подключить скомпилированный ресурс к своему проекту (предполагается, что проект уже содержит
компонент TouchKeyboard). Для этого создадим файл keyboard.rc с таким содержимым:
STANDARD101KEYBOARD RCDATA "STANDARD101KEYBOARD.res"
STANDARD102KEYBOARD RCDATA "STANDARD102KEYBOARD.res"
STANDARD106KEYBOARD RCDATA "STANDARD106KEYBOARD.res"
|
Подключим этот файл к проекту через "Project" -> "Add to Project..." (горячие клавиши Shift + F11).
При этом в файле проекта должна появиться строка:
{$R 'keyboard.res' 'keyboard.rc'}
|
Компилируем и наблюдаем заветную кнопку! Но это только половина дела, ведь ее еще нужно заставить работать.
type
TTouchKeyboard = class(Vcl.Touch.Keyboard.TTouchKeyboard)
private
procedure WMTouch(var Message: TMessage); message WM_TOUCH;
procedure ProcessKeyPress(const Msg: TWmMouse; ID: Integer);
function TouchInputToWmMouse(Control: TWinControl; const TouchInput: TouchInput): TWMMouse;
protected
procedure WndProc(var Message: TMessage); override;
end;
implementation
procedure TTouchKeyboard.WMTouch(var Message: TMessage);
var
TouchInputs: array of TouchInput;
LTouchInput: TouchInput;
TouchCount: Integer;
begin
TouchCount := LoWord(Message.WParam);
SetLength(TouchInputs, TouchCount);
if GetTouchInputInfo(Message.LParam, TouchCount, @TouchInputs[0], SizeOf(TouchInput)) then
begin
if Length(TouchInputs) = 1 then
begin
LTouchInput := TouchInputs[0];
ProcessKeyPress(TouchInputToWmMouse(Self, LTouchInput), LTouchInput.dwID);
end;
end;
inherited;
end;
procedure TTouchKeyboard.WndProc(var Message: TMessage);
begin
if not ((ssTouch in MouseOriginToShiftState) and CheckWin32Version(6, 1)) then
begin
if Message.Msg = WM_LBUTTONDBLCLK then
Dec(Message.Msg, WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
ProcessKeyPress(TWMMouse(Message), 0);
end;
inherited;
end;
procedure TTouchKeyboard.ProcessKeyPress(const Msg: TWmMouse; ID: Integer);
function FindButton(Point: TPoint): TCustomKeyboardButton; overload;
var
Index: Integer;
begin
Result := nil;
for Index := 0 to ButtonsCount-1 do
if Buttons[Index].BoundsRect.Contains(Point) then
begin
Result := Buttons[Index];
Break;
end;
end;
const
INPUTLANGCHANGE_FORWARD = $0002;
INPUTLANGCHANGE_BACKWARD = $0004;
var
Button: TCustomKeyboardButton;
begin
if not Enabled or (csDesigning in ComponentState) then
Exit;
case Msg.Msg of
WM_LBUTTONUP:
begin
Button := FindButton(SmallPointToPoint(Msg.Pos));
{.$DEFINE CHECK_VK}
if Button <> nil then
{$IFDEF CHECK_VK}
if Length(Button.Key.ComboKeys) = 1 then
if (Button.Key.ComboKeys[0].Vk = -255) and
(Button.Key.ComboKeys[0].ScanCode = -255) then
{$ELSE}
if Button.Key.ScanCode = -255 then
{$ENDIF}
if (GetAsyncKeyState(VK_SHIFT) shr 16) and 1 = 1 then
PostMessage(Application.Handle, WM_INPUTLANGCHANGEREQUEST,
INPUTLANGCHANGE_FORWARD, 0)
else
PostMessage(Application.Handle, WM_INPUTLANGCHANGEREQUEST,
INPUTLANGCHANGE_BACKWARD, 1);
end;
end;
end;
function TTouchKeyboard.TouchInputToWmMouse(Control: TWinControl;
const TouchInput: TouchInput): TWMMouse;
var
P: TPoint;
begin
Result.Msg := WM_NULL;
if TouchInput.dwFlags and TOUCHEVENTF_DOWN <> 0 then
Result.Msg := WM_LBUTTONDOWN
else
if TouchInput.dwFlags and TOUCHEVENTF_UP <> 0 then
Result.Msg := WM_LBUTTONUP
else
if TouchInput.dwFlags and TOUCHEVENTF_MOVE <> 0 then
Result.Msg := WM_MOUSEMOVE;
P := Point(TouchInput.X div 100, TouchInput.Y div 100);
PhysicalToLogicalPoint(Control.Handle, P);
Result.Pos := PointToSmallPoint(Control.ScreenToClient(P));
end;
|
Как обычно я воспользуюсь приемом с подменой класса.
Нужно предусмотреть различные варианты взаимодействия пользователя с клавиатурой - как с помощью мыши, так и при
помощи пальца на планшетах с сенсорным экраном. Для удобства и единообразия все тачи (нажатия пальцем) преобразуются
в события мыши, и обрабатываются в одном методе. Что именно обрабатывать, пару ScanCode и Vk, или только ScanCode,
в данном случае не принципиально, в примере я показал оба варианта.
Теперь, умея модифицировать TouchKeyboard, можно добавлять на нее и другие кнопки, например Windows.
Обработчик ее нажатия очень прост:
SendMessage(Self.Handle, WM_SYSCOMMAND, SC_TASKLIST, 0);
|
Сложность здесь будет заключаться в отрисовке логотипа на кнопке. Можно посмотреть, как эта задача решается в самом
компоненте, и разобраться с процедурой TCustomKeyboardButton.Paint (и ее подпрограммами PaintTab, PaintEnter,
PaintTallEnter, PaintBackspace, PaintShift и PaintArrow) в модуле Vcl.Touch.Keyboard. Впрочем, это уже совсем другая
история.
Ну вот, пользоваться приложением с модифицированной клавиатурой стало гораздо удобнее, а значит цель достигнута.
На этом все, заботьтесь о своих пользователях!
.: Пример к данной статье :.
|
При использовании материала - ссылка на сайт обязательна
|
|