-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathApus.Engine.BitmapStyle.pas
More file actions
318 lines (286 loc) · 10.2 KB
/
Apus.Engine.BitmapStyle.pas
File metadata and controls
318 lines (286 loc) · 10.2 KB
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// Стандартный стиль для UI, позволяющий определять внешний вид элементов
// с использованием изображений
//
// Copyright (C) 2006 Apus Software (ivan@apus-software.com)
// This file is licensed under the terms of BSD-3 license (see license.txt)
// This file is a part of the Apus Game Engine (http://apus-software.com/engine/)
{$R-}
unit Apus.Engine.BitmapStyle;
interface
uses Apus.Engine.API,
Apus.Engine.UI;
type
TButtonState=(bsNormal,bsOver,bsDown,bsDisabled);
TBitmapStyle=class
// Этот метод нужно переопределить в модуле проекта
// Если img=nil - нужно выделить память, иначе - просто перерисовать
procedure BuildButtonImage(btn:TUIButton;state:TButtonState;var img:TTexture); virtual;
// Возможно переопределение для изменения дефолтной отрисовки
procedure DrawItem(con:TUIElement); virtual;
// Возвращает время перехода в/из подсвеченного состояния
// (param=0 - убираниие подстветки, 1 - появление)
function AnimationTime(con:TUIElement;param:integer):integer; virtual;
end;
procedure InitBitmapStyle(styleID:integer;style:TBitmapStyle);
// Удаляет ранее созданные изображения кнопки (если они более неактуальны)
procedure DeleteButtonImages(btn:TUIElement);
procedure DeleteAllButtonImages;
implementation
uses SysUtils,
Apus.Common, Apus.AnimatedValues, Apus.EventMan, Apus.Colors, Apus.Structs, Apus.Publics,
Apus.Engine.ImageTools, Apus.Engine.UIRender;
type
// Для каждого элемента UI хранится такая структура с картинками и прочими сведениями
TButtonData=record
width,height:integer; // текущий размер кнопки
imgNormal,imgOver,imgDown,imgDisabled:TTexture;
dynamicImg:boolean; // обновлять изображения каждый кадр?
overState:TAnimatedValue; // состояние Over (0..1)
lastCaption:string;
end;
TLabelData=record
prevCaption:string;
img:TTexture;
end;
var
styleCls:TBitmapStyle;
buttons:array[1..200] of TButtonData;
bCount:integer;
btnHash:THash; // button name -> index of TButtonData
lHash:THash; // labelHash
imgHash:THash; // images hash: filename -> TTexture object (cardinal)
crSect:TMyCriticalSection;
procedure BitmapStyleHandler(control:TUIElement);
begin
styleCls.DrawItem(control);
end;
procedure EventHandler(event:TEventStr;tag:integer);
var
name:string;
btn:TUIElement;
begin
event:=UpperCase(event);
if pos('BITMAPSTYLE\INVALIDATE\',event)=1 then begin
LogMessage(event+' '+inttohex(cardinal(tag),8));
name:=event;
delete(name,1,23);
if (name='*') or (name='ALL') then begin
DeleteAllButtonImages;
end else begin
btn:=FindControl(name,false);
if btn<>nil then DeleteButtonImages(btn);
end;
end;
end;
procedure InitBitmapStyle(styleID:integer;style:TBitmapStyle);
begin
LogMessage('Init bitmap style');
InitCritSect(crSect,'BitmapStyle',70);
SetEventHandler('BitmapStyle',@eventHandler,emMixed); /// заменить sync на mixed!
styleCls:=style;
RegisterUIStyle(styleID,BitmapStyleHandler);
btnHash.Init;
imgHash.Init;
bCount:=0;
fillchar(buttons,sizeof(buttons),0);
end;
// x,y - button center
procedure DrawBtnImage(btn:TUIButton;state:TButtonState;xc,yc:single;var img:TTexture;color:cardinal);
var
x,y:integer;
begin
if (img=nil) or (img.name='OUT_OF_DATE') then
styleCls.BuildButtonImage(btn,state,img);
x:=round(xc+0.1); y:=round(yc+0.1);
if img<>nil then begin
draw.Centered(x,y,img,color);
end else begin
dec(x,btn.globalrect.width div 2);
dec(y,btn.globalrect.height div 2);
draw.FillGradrect(x,y,x+btn.globalrect.width-1,y+btn.globalrect.height-1,$FFE0E0EE,$FFB0B0C0,true);
draw.Rect(x,y,x+btn.globalrect.width-1,y+btn.globalrect.height-1,$A0FFFFFF);
end;
end;
// x,y - button center
procedure DrawBtnImageInt(btn:TUIButton;xc,yc:single;var imgNormal,imgOver:TTexture;color:cardinal;intFactor:single);
var
x,y:integer;
begin
if (imgNormal=nil) or (imgNormal.name='OUT_OF_DATE') then
styleCls.BuildButtonImage(btn,bsNormal,imgNormal);
if (imgOver=nil) or (imgOver.name='OUT_OF_DATE') then
styleCls.BuildButtonImage(btn,bsOver,imgOver);
if (imgNormal<>nil) and (imgOver<>nil) then begin
x:=round(xc+0.1);
y:=round(yc+0.1);
// if imgNormal.width and 1=1 then x:=round(xc+0.5);
// if imgNormal.height and 1=1 then y:=round(yc+0.5);
dec(x,imgNormal.width div 2);
dec(y,imgNormal.height div 2);
gfx.shader.TexMode(1,tblInterpolate,tblInterpolate,TTexFilter.fltBilinear,intFactor);
// draw.SetTexInterpolationMode(1,tintFactor,intFactor);
draw.DoubleTex(x,y,imgNormal,imgOver,color);
gfx.shader.TexMode(1,tblDisable,tblDisable);
end;
end;
procedure DeleteAllButtonImages;
var
i:integer;
begin
EnterCriticalSection(crSect);
LogMessage('Deleting all button images!');
try
for i:=1 to bCount do
with buttons[i] do begin
FreeImage(imgNormal);
FreeImage(imgOver);
FreeImage(imgDown);
FreeImage(imgDisabled);
end;
finally
LeaveCriticalSection(crSect);
end;
end;
procedure DeleteButtonImages(btn: TUIElement);
var
idx:integer;
begin
EnterCriticalSection(crSect);
try
idx:=btnHash.Get(btn.name);
if idx>0 then
with buttons[idx] do begin
FreeImage(imgNormal);
FreeImage(imgOver);
FreeImage(imgDown);
FreeImage(imgDisabled);
end;
finally
LeaveCriticalSection(crSect);
end;
end;
procedure InvalidateImages(bData:integer);
begin
EnterCriticalSection(crSect);
with buttons[bData] do try
if imgNormal<>nil then imgNormal.name:='OUT_OF_DATE';
if imgOver<>nil then imgOver.name:='OUT_OF_DATE';
if imgDown<>nil then imgDown.name:='OUT_OF_DATE';
if imgDisabled<>nil then imgDisabled.name:='OUT_OF_DATE';
finally
LeaveCriticalSection(crSect);
end;
end;
procedure TBitmapStyle.DrawItem(con: TUIElement);
var
i,j:integer;
enabl:boolean;
x1,y1,x2,y2,a:integer;
xc,yc:single;
bData:integer;
st:string;
c,btnColor:cardinal;
img:TTexture;
btn:TUIButton;
begin
// Определение общих св-в элемента
enabl:=con.enabled; // для отрисовки кнопок доступность предков значения не имеет
with con.globalrect do begin
x1:=Left; x2:=right-1;
y1:=top; y2:=bottom-1;
xc:=(x1+x2)/2;
yc:=(y1+y2)/2;
end;
EnterCriticalSection(crSect);
try
// Кнопка
if (con.ClassType=TUIButton) or (con.classtype=TUIComboBox) then
with con as TUIButton do begin
bData:=btnHash.Get(con.name);
if bData=0 then begin
inc(bCount);
bData:=bCount;
btnHash.Put(con.name,bData);
buttons[bData].overState.Init(byte(underMouse=con));
buttons[bData].lastCaption:=TUIButton(con).caption;
end else begin
if buttons[bData].lastCaption<>TUIButton(con).caption then begin
if pos('%UPDATE%',TUIButton(con).caption)=1 then delete(TUIButton(con).caption,1,8);
// caption changed - invalidate images
buttons[bData].lastCaption:=TUIButton(con).caption;
InvalidateImages(bData);
end;
end;
// Разместить кнопку с привязкой к правому краю
if con.classtype=TUIComboBox then begin
xc:=x2-round(con.globalrect.height/2);
end;
btnColor:=TUIButton(con).color;
btnColor:=$FF808080; // пока так - чтобы не поломать везде всё
with buttons[bData] do
if enabl then begin // Button enabled
if pressed then begin
DrawBtnImage(TUIButton(con),bsDown,xc,yc,imgDown,btnColor);
// overState.Assign(0);
end else begin // Not pressed
if (underMouse=con) and (overState.FinalValue<>1) then
overState.Animate(1,styleCls.AnimationTime(con,1),Spline0);
if (underMouse<>con) and (not overState.IsAnimating) and (overState.Value<>0) then
overState.Animate(0,styleCls.AnimationTime(con,0),Spline0);
a:=round(255*overState.Value);
if a>0 then begin
if FindConst(con.styleinfo+'\overlay')>=0 then begin
// btnOver image is an overlay image
DrawBtnImage(TUIButton(con),bsNormal,xc,yc,imgNormal,btnColor);
DrawBtnImage(TUIButton(con),bsOver,xc,yc,imgOver,btnColor and $FFFFFF+a shl 24);
end else begin
// btnOver image is a standalone image
if a<255 then
DrawBtnImageInt(TUIButton(con),xc,yc,imgNormal,imgOver,btnColor,1-a/255)
else
DrawBtnImage(TUIButton(con),bsOver,xc,yc,imgOver,btnColor);
end;
end else
DrawBtnImage(TUIButton(con),bsNormal,xc,yc,imgNormal,btnColor);
end;
end else begin // Disabled
DrawBtnImage(TUIButton(con),bsDisabled,xc,yc,imgDisabled,btnColor);
overState.Assign(0);
end;
end;
if con.ClassType=TUILabel then
with con as TUILabel do begin
st:=lHash.Get(con.name);
if st<>caption then begin
// Redraw
end;
if caption<>'' then lHash.Put(con.name,caption,true);
end;
if con.ClassType=TUIImage then
with con as TUIImage do begin
c:=imgHash.Get(con.name);
if c=0 then begin
img:=LoadImageFromFile(TUIImage(con).src);
imgHash.Put(con.name,cardinal(img),true);
end else
img:=pointer(c);
// draw.RotScaled(xc,yc,img,TUIImage(con).color);
end;
finally
LeaveCriticalSection(crSect);
end;
end;
{ TBitmapStyle }
function TBitmapStyle.AnimationTime(con: TUIElement; param: integer): integer;
begin
case param of
0:result:=100;
1:result:=200;
end;
end;
procedure TBitmapStyle.BuildButtonImage(btn: TUIButton; state: TButtonState;
var img: TTexture);
begin
img:=nil;
end;
end.