Skróty klawiaturowe

Adam Boduch

W tym artykule zaprezentuje prosty sposób na obslugę skrótów klawiaturowych. Naprawdę nie jest to trudne gdyż VCL udostępnia nam zestaw pomocnych procedur które operują na skrótach właśnie. Na samym początku należy wspomnieć o samym typie zmiennej:

TShortCut - jest to typ zmiennej skrótu klawiaturowego.

VCL udostępnia przyjemną funkcję do przekształcania znaku na skrót:

function ShortCut(Key: Word; Shift: TShiftState): TShortCut;

Uwaga! Żeby korzystać z tej procedury dodaj do listy uses moduł Menus. Pierwszym parametrem tej funkcji jest kod ASCII klawisza - np A - kod ASCII: #65. Kolejny parametr to stan klawiszy Ctrl, Alt, Shift. Mogą one być wcisnięte lub nie, prawda? Funkcja zwraca zmienną w postaci skrótu (ależ się powtarzam z tym słowem "skrót" :)).

Na samym początku budowania naszej przykładowej aplikacji należy napisać procedurę obsługi zdarzenia OnKeyDown. Powodować ona będzie wyświetlenie w komponencie edtShortCut (typu TEdit) wciśniętego klawisza:

{ wpisz nacisniety skrot w komponencie TEdit }
  edtShortCut.Text := ShortCutToText(ShortCut(Key, Shift));

Jak widzisz wykorzystałem tutaj poznaną Ci już funkcję ShortCut. Użyta tutaj zostaje także komenda mówiąca o konwersji skrótu w postaci zmiennej TShortCut na tekst, który może być następnie przypisany komponentowi edtShortCut. to już mamy - umiemy już wyświetlać naciskaną kombinację klawiszy w komponencie Edit. Teraz czeka Cię nieco trudniejsze zadanie, ale... nie przerażaj się - nie będzie aż tak trudne :)

W sekcji interface programu wpisz takie coś:

  TProcedure = procedure of object;

{ rekord, ktory przechowuje informacje o skrocie klawiaturowym i o procedurze
  ktora bedzie wykonywana w nastepstwie nacisniecia takowego skrotu }
  TShortCutArray = packed record
    sShortCut: TShortCut;
    pAction : TProcedure;
  end;

var
  ShortCutArray : array[0..1] of TShortCutArray; // tablica rekordow

Pierwsza linia to deklaracja nowego typu - procedury :) PóĽniej tworzę rekord, który przechowywał będzie: skrót klawiaturowy do obsłużenia przez naszą aplikację oraz procedure obsługi zdarzenia, czyli mówiąc po ludzku "co się stanie jeżeli użytkownik naciśnię określoną kombinację klawiszy". I na końcu deklaruje tablice rekordów. W tablicy przechowywane będą skróty do obsłużenia. Wygeneruj procedurę [b]OnCreate [/b]formy i wpisz w niej takie coś:

procedure TMainForm.FormCreate(Sender: TObject);
begin
{ przypisujemy do tablicy dane. Pierwszy element - skrot do obsluzenia
  oraz procedura ktora bedzie wykonywana w jego nastepstwie }
  ShortCutArray[0].sShortCut := TextToShortCut('Ctrl+A');
  ShortCutArray[0].pAction := Procedura1;

  ShortCutArray[1].sShortCut := TextToShortCut('Ctrl+D');
  ShortCutArray[1].pAction := Procedura2;
end;

W pierwszym elemencie tablicy wpisujemy skrót do obsłużenia - w tym wypadku Ctrl+A. Następnie procedurę, która będzie wykonywana w chwili gdy ktoś naciśnie Ctrl+A. to już właściwie koniec programu - została jeszcze do napisania jedna procedura. Procedura, która będzie analizowała naciśnięte klawisze. A oto i ona:

procedure TMainForm.Action(wKey: Word; sShift: TShiftState);
var
  i : Integer;
  pKey : Word;
  pShift : TShiftState;
begin
{
  petla analizuje kazdy element tablicy ShortCutArray. Nastepnie nastepuje
  rozbicie skrotu z tablicy na dwie zmienne - pKey oraz pShift. pKey oznacza
  kod ASCII klawisza a pShift nacisniecie klawisza Ctrl, Alt lub Shift.
  Nastepnie zmienna pKey jest porownywana ze zmienna (parametrem) wKey.
  Jezeli rowniez zmienne pShift oraz sShift sa takie same to wywolywana
  zostaje procedura, ktora zadeklarowana zostala w rekordzie
}
  for I := 0 to High(ShortCutArray) do
  begin
  { rozbicie skrotu z tablicy }
    ShortCutToKey(ShortCutArray[i].sShortCut, pKey, pShift);
    { porownanie zmiennych }
    if ((pKey = wKey) and (sShift = pShift)) then
      ShortCutArray[i].pAction; // wywolanie odpowiedniej procedury
  end;
end;

Ma ona za zadanie przeszukanie całej tablicy ShortCutArray. Co ona właściwie robi? Popatrzmy: wywołanie procedury ShortCutToKey. Procedura ta robija skrót klawiaturowy na dwie części: kod ASCII klawisza który został nacisnięty oraz czy w tym czasie był wciśnięty klawisz Shift, Ctrl lub Alt. Tak więc, rozbija ona skrót na dwie części i przypisuje do zmiennych: pKey oraz pShift. PóĽniej porównuje te dwie zmienne ze zmiennymi przekazanymi w parametrze procedury [b]Action[/b]. Jeżeli zmienne są identyczne to następuje wywołanie procedury zdarzeniowej dla danego skrótu... :) to już jest koniec. Procedura Action musi być wywołana podczas gdy ktoś naciśnie klaisz więc wpisz ją do obsługi OnKeyDown:

procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
{ wpisz nacisniety skrot w komponencie TEdit }
  edtShortCut.Text := ShortCutToText(ShortCut(Key, Shift));
{ wywolaj z parametrami procedure Action }
  Action(Key, Shift);
end;

Aby łatwiej Ci było wszystko pojąć oto cały listing programu:

(************************************************************************)
(*                                                                      *)
(*                   Copyright (c) 2002 by Adam Boduch                  *)
(*                     mailto:adam@4programmers.net                     *)
(*                        www.4programmers.net                          *)
(*                                                                      *)
(************************************************************************)

unit MainFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Menus;

type
  TMainForm = class(TForm)
    edtShortCut: TEdit;
    Label1: TLabel;
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormCreate(Sender: TObject);
  private
  { to sa procedury ktore beda wykonywane gdy ktos wcisnie skrot }
    procedure Procedura1;
    procedure Procedura2;
  { procedura, ktora zanalizuje kazde nacisniecie klawisza }  
    procedure Action(wKey : Word; sShift : TShiftState);
  end;

  TProcedure = procedure of object;

{ rekord, ktory przechowuje informacje o skrocie klawiaturowym i o procedurze
  ktora bedzie wykonywana w nastepstwie nacisniecia takowego skrotu }
  TShortCutArray = packed record
    sShortCut: TShortCut;
    pAction : TProcedure;
  end;

var
  ShortCutArray : array[0..1] of TShortCutArray; // tablica rekordow

var
  MainForm: TMainForm;

implementation

{$R *.DFM}

procedure TMainForm.FormCreate(Sender: TObject);
begin
{ przypisujemy do tablicy dane. Pierwszy element - skrot do obsluzenia
  oraz procedura ktora bedzie wykonywana w jego nastepstwie }
  ShortCutArray[0].sShortCut := TextToShortCut('Ctrl+A');
  ShortCutArray[0].pAction := Procedura1;

  ShortCutArray[1].sShortCut := TextToShortCut('Ctrl+D');
  ShortCutArray[1].pAction := Procedura2;
end;

procedure TMainForm.Action(wKey: Word; sShift: TShiftState);
var
  i : Integer;
  pKey : Word;
  pShift : TShiftState;
begin
{
  petla analizuje kazdy element tablicy ShortCutArray. Nastepnie nastepuje
  rozbicie skrotu z tablicy na dwie zmienne - pKey oraz pShift. pKey oznacza
  kod ASCII klawisza a pShift nacisniecie klawisza Ctrl, Alt lub Shift.
  Nastepnie zmienna pKey jest porownywana ze zmienna (parametrem) wKey.
  Jezeli rowniez zmienne pShift oraz sShift sa takie same to wywolywana
  zostaje procedura, ktora zadeklarowana zostala w rekordzie
}
  for I := 0 to High(ShortCutArray) do
  begin
  { rozbicie skrotu z tablicy }
    ShortCutToKey(ShortCutArray[i].sShortCut, pKey, pShift);
    { porownanie zmiennych }
    if ((pKey = wKey) and (sShift = pShift)) then
      ShortCutArray[i].pAction; // wywolanie odpowiedniej procedury
  end;
end;

procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
{ wpisz nacisniety skrot w komponencie TEdit }
  edtShortCut.Text := ShortCutToText(ShortCut(Key, Shift));
{ wywolaj z parametrami procedure Action }
  Action(Key, Shift);
end;

procedure TMainForm.Procedura1;
begin
// wywolywana gdy ktos nacisnie Ctrl+A
  ShowMessage(
  'Ha! Wywołano mnie. to znaczy, że nacisnełeś Ctrl+A');
end;

procedure TMainForm.Procedura2;
begin
// wywolywana gdy ktos nacisnie Ctrl+D
  ShowMessage(
  'Ha! Wywołano mnie. to znaczy, że nacisnełeś Ctrl+D');
end;

end.

Jest jeszcze oprócz tego jedna pomocna procedura, której nie miałem zaszczytu omówić w tym artykule. Jest to:

function TextToShortCut(Text: string): TShortCut;

Powoduje ona przekształcenie string'a do postaci zmiennej typu TShortCut.

3 komentarzy

przydało by sie kolorowanko :).

W czasie walki z tym ustrojstwem wyszedl drugi problem. Jak juz mam Edita jak zrobic zeby zdarzenie KeyDown dziedziczyl on z formy glownej? Bo wychodzi mi ze jak juz jestem w Edit to klawisz skrotu dla formy nie dziala, Edit ma
wlasny event KeyDown. Prosze pomozcie.

Witam
Gdy mam na MainForm komponetnt Edit lub nawet Button u mnie to nie działa. Gdy zamiast Edita uzyje Labela to wszystko dziala wysmienicie. W czym moze byc problem? Wydaje mi sie ze gdy mam Edit lub Button focus automatycznie laduje
na ktoryms z tych komponentow i program nie wykrywa ze na MainForm zostal wcisniety klawisz. Jak to przerobic zeby
dzialalo zawsze? Malo uzyteczne gdy skroty dzialaja tylko przy pustej formie.