Autor Beitrag
Logikmensch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Fr 23.12.05 07:00 
Hallo, liebes Delphi-Forum-Team,

ich arbeite seit geraumer Zeit an einer besonderen Textverarbeitung, die im Grunde auch hervorragend arbeitet. Allerdings habe ich das Problem, dass die Tastaturabfrage nicht ganz so hinhaut, wie ich mir das vorstelle. Und zwar wird das Loslassen von Tasten genau dann nicht erfasst, sobald man z.B. einen Dialog öffnet, z.B. durch Drücken eines Shortcuts. Um diese Frage nicht unnötig aufzublähen, habe ich mein Problem mal extrahiert und hier in ein kleines Demoprogramm reingepackt, das ein Gitter für alle 256 Tasten anzeigt. Man kann auch mehr als eine Taste auf einmal drücken - im Gitter werden die gedrückten Tasten rot angezeigt. Beim loslassen einer Taste wird die korrespondierende Gitterzelle wieder weiß. Soweit so gut. Hier erst einmal der Code (für Delphi 7, 2005 etc.). Man benötigt nur ein leeres Formular:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    procedure FormDestroy(Sender: TObject);
    procedure StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
    keys:array[0..255] of boolean;
    grid:TStringGrid;
    menu:TMainMenu;
    procedure Menu_Datei(Sender: TObject);
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  x,y:integer;
  m:TMenuItem;
begin
  //StringGrid initialisieren und mit 256 Werten füllen:
  grid:=TStringGrid.Create(self);
  grid.Parent:=self;
  with grid do begin
    align:=alClient;
    defaultdrawing:=false;
    ondrawcell:=StringGridDrawCell;
    fixedcols:=0;
    fixedrows:=0;
    colcount:=16;
    rowcount:=16;
    for y:=0 to 15 do
      for x:=0 to 15 do
        cells[x,y]:=inttostr(y*16+x);
    for x:=0 to 15 do
      colwidths[x]:=32;
    tabstop:=false;
  end; {with}
  //Tastatur-Merker initialisieren:
  fillchar(keys,sizeof(keys),0);
  //Hauptmenü erzeugen:
  menu:=TMainMenu.Create(self);
  m:=TMenuItem.Create(self);
  m.OnClick:=Menu_Datei;
  //Der Menüeintrag soll sowohl auf ALT+D als auch auf STRG+F reagieren:
  m.Caption:='&Datei';
  m.ShortCut:=ShortCut(Word('F'), [ssCtrl]);
  menu.Items.Add(m);
end;

procedure TForm1.Menu_Datei;
begin
  showmessage('Dies wäre der Datei-Dialog');
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if key<=255 then keys[key]:=true;
  grid.Invalidate;
end;

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if key<=255 then keys[key]:=false;
  grid.Invalidate;
end;

procedure TForm1.StringGridDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  key:integer;
begin
  key:=ARow*16+ACol;
  with grid,canvas do begin
    brush.Style:=bsSolid;
    if keys[key] then brush.Color:=clRed
      else brush.Color:=clWindow;
    textrect(rect,rect.Left,rect.Top,cells[ACol,ARow]);
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  grid.Free;
  menu.Free;
end;

end.


Nun das Problem: Wenn man z.B. den kleinen Test-Menüpunkt "Datei" durch Drücken von ALT+D oder STRG+F aufruft, erscheint die kleine Messagebox und wenigstens eine der Systemtasten ALT oder STRG bleibt gedrückt. Einfach deswegen, weil das Formular das entsprechende KeyUp-Ereignis nicht mehr bekommt, weil es nicht mehr den Focus hat.
Ich möchte aber, dass das Formular diese Tastenloslasser trotzdem mitbekommt. Habt ihr da eine Idee?
Es nützt mir nämlich gar nichts, auf das Deaktivieren des Fensters zu reagieren und dort meine Tastaturmerker zu löschen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure TForm1.FormDeactivate(Sender: TObject);
var
  i:integer;
begin
  fillchar(keys,sizeof(keys),0);
end;


Weil dieser Event leider nicht ausgelöst wird, sobald eine Message oder ein anderer Dialog aufgerufen wird.

Für Ideen oder Anregungen wäre ich SEHR dankbar!

Liebe Grüße,

Claus.
NeoInDerMATRIX
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 245

Win95, Win98(+se), WinNT, Win2000, WinME, WinXP(+pro), VISTA, Linux(SuSe), DOS [MultiMon(3)], Vista
D6 PeE + (FP 2.0l) + D3 Pe + D2005+ D2006 Arch
BeitragVerfasst: Sa 24.12.05 00:06 
Hi,

dafür must du nur die Nachrichten die dir Windows sendet in der Application abfangen.

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
Unit KeyMapper;

Interface

Uses
    Windows,
    Forms;

Type
    TKeyMapper = Class
    Private
        App: TApplication;
        Key: Array[0..256] Of Boolean;
    Public
        Constructor Create(AApp: TApplication);
        Destructor Destroy; Override;
        Procedure WndProc(Var Msg: TMsg; Var Handled: Boolean);
    End;

Implementation

Uses
    SysUtils,
    Messages;

Constructor TOCtrlKeyMapper.Create(AApp: TApplication);
Begin
    FillChar(Key, SizeOf(Key), False);
    AApp.OnMessage := WndProc;
    App := AApp;
End;

Destructor TOCtrlKeyMapper.Destroy;
Begin
    App.OnMessage := Nil;
    App := Nil;
End;

Procedure TOCtrlKeyMapper.WndProc(Var Msg: TMsg; Var Handled: Boolean);
Begin
    Case Msg.Message Of
        wm_KeyUp:
            Self.Key[Msg.wParam] := False;
        wm_KeyDown:
            Self.Key[Msg.wParam] := True;
    End;
End;


Einbinden am besten in der MainForm mit:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
Type
 tForumX = Class(...)
  ...
  KeyMapper: tOCtrlKeyMapper;
  ...
 End;

Function tFormX.onCreate(...);
Begin
 KeyMapper:=tKeyMapper.Create(Application);
End;

Damit sollten die Klasse KeyMapper, immer aktuelle werte haben.

Cu
Neo
F34r0fTh3D4rk
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 5285
Erhaltene Danke: 29

Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
BeitragVerfasst: Sa 24.12.05 09:27 
warum benutzt du dann nicht getasynckeystate ?
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: So 25.12.05 21:54 
Probier doch mal die TActionList, die sollte das eigentlich können ...

//Edit: Ansonsten schau Dir mal die OCtrlKeyMapper.pas an.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.


Zuletzt bearbeitet von BenBE am So 25.12.05 22:00, insgesamt 1-mal bearbeitet
NeoInDerMATRIX
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 245

Win95, Win98(+se), WinNT, Win2000, WinME, WinXP(+pro), VISTA, Linux(SuSe), DOS [MultiMon(3)], Vista
D6 PeE + (FP 2.0l) + D3 Pe + D2005+ D2006 Arch
BeitragVerfasst: So 25.12.05 21:54 
Hi,

Tasten Polling, ich dachte wir sind aus dem Zeitalter von Dos raus!
Diese Methode die ich hier aufzeige ist die einfachste die ich kenne. Polling sollte man in der heutigen Zeit nur für bereiche anwenden wo es nicht anderst geht.

Cu
Neo
Logikmensch Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Mo 26.12.05 21:01 
Titel: Vielen Dank!
Hallo ihr Denker! :-)

Besonderen Dank für die Ideen und Anregungen. Besonders während der Weihnachtsfeiertage, an denen man doch nicht immer am PC hängt. *grins*
In der Tat hatte ich ganz zu Anfang mit GetAsyncKeyState gearbeitet, aber war mit den Ergebnissen in meinem Code mehr als unzufrieden und habe mich dann mit dem KEYS-Array eigentlich recht wohl gefühlt. Ich denke, NeoInDerMatrix hat eine gute Lösung, die Windows-Botschaften ganz unten abzufangen. Vielen Dank für den Code. Ich kann es kaum erwarten, es auszuprobieren - bin im Moment verreist und habe meinen Delphi-PC nicht mit.

Es bedankt sich ein Entenbraten-gestärkter Claus! :D

P.S.: BenBE: Ich konnte OCtrlKeyMapper.pas nicht runterladen, ich versuche es später erneut. Danke auch für den Tipp!
NeoInDerMATRIX
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 245

Win95, Win98(+se), WinNT, Win2000, WinME, WinXP(+pro), VISTA, Linux(SuSe), DOS [MultiMon(3)], Vista
D6 PeE + (FP 2.0l) + D3 Pe + D2005+ D2006 Arch
BeitragVerfasst: Di 27.12.05 04:09 
Hi,

Der Link den BenBE dir gepostet hat ist kein DL sondern er zeigt dir den Source im Browser an! Soweit ich mich erinnere. Aber dieser Source ist genau das gleiche nur halt für ein game angepasst. (Ist nähmlich von mir.)
Fals du noch fragen haben solltest BenBE und Ich helfen dir sehr gerne.
Würde mich freuen wenn du mich über dein Forankommen Informieren würdest.

Cu
Neo
Logikmensch Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Di 03.01.06 05:43 
Titel: Vielen Dank!
Hallo und allen ein frohes neues Jahr!

Nochmals danke für die Tipps. Ich habe mit eurer Hilfe geschafft, das Problem zu beseitigen. :D

Liebe Grüße,

Claus