-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathFMX.CustomCursors.Win.pas
234 lines (198 loc) · 7.51 KB
/
FMX.CustomCursors.Win.pas
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
{*******************************************************}
{ }
{ Delphi FireMonkey Platform Extensions }
{ }
{ Written by Ken Schafer - released as is }
{ with no warrantees or promises }
{ }
{ USE AT YOUR OWN RISK }
{ }
{*******************************************************}
unit FMX.CustomCursors.Win;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Rtti, System.Classes,
System.IOUtils, System.Variants, FMX.Platform.win, FMX.Types,Winapi.Windows;
Type
TCustomCursorPlatformWin = class(TInterfacedObject, IFMXCursorService)
private
FCursor: TCursor;
// FCustomCursor: TCustomCursor;
public
procedure SetCursor(const ACursor: TCursor);
function GetCursor: TCursor;
end;
TCursorInfo = record
TheCursorIdent:Integer;
TheCursor:HCURSOR;
end;
TCustomCursors = record
private
CursorInfo:Array of TCursorInfo;
function GetCursors(CursorIdent: Integer): HCursor;
procedure SetCursor(CursorIdent: Integer; const Value: HCursor);
procedure ReleaseCursors;
public
procedure LoadCursor(CursorIdent:Integer;InCursorName:string; HotSpotX:Single=0;HotSpotY:Single=0); overload;
procedure LoadCursor(CursorIdent:Integer;InCursorName:string; HotSpot:TPointF); overload;
procedure LoadAnimatedCursor(CursorIdent:Integer;InCursorName:string;
HotSpotX:Single=0;HotSpotY:Single=0);
function HasCursor(CursorIdent:Integer):Boolean;
property Cursors[CursorIdent:Integer]:HCursor read GetCursors write SetCursor;
end;
TCustomCursorCursorService = TCustomCursorPlatformWin;
var
TrueCursorController:TCustomCursors;
implementation
uses
System.math, FMX.Forms, FMX.CustomCursors;
{ TPlatformExtensionsWin }
procedure TCustomCursorPlatformWin.SetCursor(const ACursor: TCursor);
const
CustomCursorMap: array [crSizeAll .. crNone] of PChar = (
nil, nil, nil, nil, nil, IDC_SQLWAIT, IDC_MULTIDRAG, nil, nil, IDC_NODROP, IDC_DRAG, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil);
CursorMap: array [crSizeAll .. crNone] of PChar = (
IDC_SIZEALL, IDC_HAND, IDC_HELP, IDC_APPSTARTING, IDC_NO, nil, nil, IDC_SIZENS, IDC_SIZEWE, nil, nil, IDC_WAIT,
IDC_UPARROW, IDC_SIZEWE, IDC_SIZENWSE, IDC_SIZENS, IDC_SIZENESW, IDC_SIZEALL, IDC_IBEAM, IDC_CROSS, IDC_ARROW, nil);
function IsDefaultOrInvalidCursor(const ACursor: TCursor): Boolean;
begin
Result := (ACursor = crDefault) or not InRange(ACursor, crSizeAll, crNone);
end;
var
NewCursor: HCURSOR;
function KeyIsDown(KeyInQuestion:Integer):Boolean;
begin
result:=GetAsyncKeyState(KeyInQuestion) AND $FF00 <> 0;
end;
function vkLeftButton:Word;
begin
if GetSystemMetrics(SM_SWAPBUTTON) <> 0 then
result:=vkRButton
else
result:=vkLButton;
end;
begin
//kjs added key check
if not (DragAndDropIsActive and KeyIsDown(vkLeftButton)) then
begin
if TrueCursorController.HasCursor(ACursor) then
begin
WinAPI.Windows.SetCursor(TrueCursorController.Cursors[ACursor]);
exit;
end;
// We don't set cursor by default, when we create window. So we should use crArrow cursor by default.
if IsDefaultOrInvalidCursor(ACursor) and not (csDesigning in Application.ComponentState) then
FCursor := crArrow
else
FCursor := ACursor;
if InRange(FCursor, crSizeAll, crNone) then
begin
if CustomCursorMap[FCursor] <> nil then
NewCursor := LoadCursorW(HInstance, CustomCursorMap[FCursor])
else
NewCursor := LoadCursorW(0, CursorMap[FCursor]);
Winapi.Windows.SetCursor(NewCursor);
end;
end;
end;
function TCustomCursorPlatformWin.GetCursor: TCursor;
begin
Result := FCursor;
end;
function TCustomCursors.HasCursor(CursorIdent: Integer): Boolean;
var
I:Integer;
begin
for I := 0 to High(CursorInfo) do
if CursorInfo[i].TheCursorIdent=CursorIdent then
begin
result:=true;
exit;
end;
result:=False;
end;
procedure TCustomCursors.LoadAnimatedCursor(CursorIdent:Integer;InCursorName:string;
HotSpotX:Single=0;HotSpotY:Single=0);
var
CursorFile: String;
TempFileName: array [0..MAX_PATH-1] of char;
TempDir: String;
begin
TempDir:=System.IOUtils.TPath.GetTempPath();
if WinAPI.Windows.GetTempFileName(PWideChar(TempDir), '~', 0, TempFileName) = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
CursorFile := TempFileName;
with TResourceStream.Create(hInstance, InCursorName, RT_ANICURSOR) do
try
SaveToFile(CursorFile);
finally
Free;
end;
Cursors[CursorIdent]:= LoadImage(0, PChar(CursorFile), IMAGE_CURSOR, 0, 0,
LR_DEFAULTSIZE or LR_LOADFROMFILE);
DeleteFile(PChar(CursorFile));
if Cursors[CursorIdent] = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
procedure TCustomCursors.LoadCursor(CursorIdent:Integer;InCursorName:string; HotSpot:TPointF);
begin
LoadCursor(CursorIdent,InCursorName,Hotspot.X,Hotspot.Y);
end;
procedure TCustomCursors.LoadCursor(CursorIdent:Integer;InCursorName:string;HotSpotX:Single=0; HotSpotY:Single=0);
var
TempFileName:TFileName;
rStream:TResourceStream;
begin
if CursorIdent < 0 then
raise Exception.Create('Cursor Idents below zero are reserved for system cursors!');
TempFileName:=System.IOUtils.TPath.GetTempFileName;
Cursors[CursorIdent]:=WinAPI.Windows.LoadCursorW(hinstance,InCursorName);
if (Cursors[CursorIdent]=0) and (FindResourceW(HInstance,PWideChar(InCursorName),RT_CURSOR) > 0) then
rStream:=TResourceStream.Create(Hinstance,InCursorName,RT_CURSOR)
else if (Cursors[CursorIdent]=0) and (FindResourceW(HInstance,PWideChar(InCursorName),RT_RCDATA) > 0) then
rStream:=TResourceStream.Create(Hinstance,InCursorName,RT_RCDATA)
else
exit;
rStream.SaveToFile(TempFileName);
rStream.Free;
Cursors[CursorIdent]:=WinAPI.Windows.LoadCursorFromFile(PWideChar(TempFileName));
DeleteFile(PWideChar(TempFileName));
end;
function TCustomCursors.GetCursors(CursorIdent: Integer): HCursor;
var
I:Integer;
begin
for I := 0 to High(CursorInfo) do
if CursorInfo[i].TheCursorIdent=CursorIdent then
begin
result:=CursorInfo[i].TheCursor;
exit;
end;
result:=WinAPI.Windows.LoadCursor(0, 'IDC_ARROW');
end;
procedure TCustomCursors.SetCursor(CursorIdent: Integer; const Value: HCursor);
var
I:Integer;
begin
if CursorIdent < 0 then
raise Exception.Create('Cursor Idents below zero are reserved for system cursors!');
for I := 0 to High(CursorInfo) do
if CursorInfo[i].TheCursorIdent=CursorIdent then
begin
CursorInfo[i].TheCursor:=Value;
exit;
end;
SetLength(CursorInfo,Length(CursorInfo)+1);
CursorInfo[High(CursorInfo)].TheCursorIdent:=CursorIdent;
CursorInfo[High(CursorInfo)].TheCursor:=Value;
end;
procedure TCustomCursors.ReleaseCursors;
begin
SetLength(CursorInfo,0);
end;
initialization
SetLength(TrueCursorController.CursorInfo,0);
finalization
TrueCursorController.ReleaseCursors;
end.