-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathavoraiser.listbox.pas
More file actions
425 lines (361 loc) · 12.2 KB
/
avoraiser.listbox.pas
File metadata and controls
425 lines (361 loc) · 12.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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
(*
Avoraiser UI Framework - High-Performance WinAPI UI for Avocado
Avoraiser is a framework for creating modern user interfaces for the Polish programming language Avocado.
Copyright (c) 2025-2026 Dymitr Wygowski (Programista Art)
Project Created: 23.12.2025
Author: Dymitr Wygowski (Programista Art)
Contact: programista.art@gmail.com
GitHub: https://github.com/Programista-Art/Avoraiser
Version: 1.0
License: Dual-Licensed: Avoraiser Community / Commercial
See LICENSE.md for terms and conditions.
Module: avoraiser.listbox
Description:
*)
unit avoraiser.listbox;
{$mode ObjFPC}{$H+}
interface
uses
Windows, avoraiser.core, avoraiser.utils;
function create_listbox(Parent: HWND; X, Y, Width, Height: Integer): HWND;
// Zarządzanie danymi
procedure list_add(h_list: HWND; const text: string);
procedure list_insert(h_list: HWND; index: Integer; const text: string);
function list_delete(h_list: HWND; index: Integer): Integer;
procedure list_delete_selected(h_list: HWND);
procedure list_clear(h_list: HWND);
// Informacje
function list_count(h_list: HWND): Integer;
function list_get_index(h_list: HWND): Integer;
function list_get_text(h_list: HWND): string;
function list_find(h_list: HWND; const text: string): Integer;
// Ukryte dane (ID)
procedure list_set_data(h_list: HWND; index: Integer; value: Integer);
function list_get_data(h_list: HWND; index: Integer): Integer;
function list_get_selected_data(h_list: HWND): Integer;
// Wygląd
procedure set_listbox_selection_color(Handle: HWND; TextColor, BgColor: DWORD);
procedure set_listbox_colors(Handle: HWND; TextCol, BgCol, SelTextCol, SelBgCol: DWORD);
procedure list_begin_update(h_list: HWND);
procedure list_end_update(h_list: HWND);
procedure draw_listbox_item(mylparam: LPARAM);
implementation
uses
avoraiser.window; // Tu jest draw_avocado_border
// Deklaracja forward, bo używamy jej w ParentProc
//procedure draw_listbox_item(l_param: LPARAM); forward;
// --- 1. NOWOŚĆ: PROCEDURA RODZICA (Hook na okno główne) ---
// Ta procedura "siedzi" na oknie głównym i wyłapuje komunikaty dla ListBoxa
function listbox_parent_proc(hwnd: HWND; msg: UINT; wparam: WPARAM; lparam: LPARAM;
u_id_subclass: UINT_PTR; dwrefdata: DWORD_PTR): LRESULT; stdcall;
var
dis: PDrawItemStruct;
my_listbox_handle: HWND;
begin
// Odczytujemy uchwyt naszego ListBoxa (przekazany jako dwRefData przy tworzeniu)
//my_listbox_handle := HWND(dw_ref_data);
my_listbox_handle := PtrUInt(dwrefdata);
//my_listbox_handle := HWND(dw_ref_data);
case msg of
// Wyłapujemy WM_DRAWITEM (Rysowanie elementu)
WM_DRAWITEM:
begin
dis := PDrawItemStruct(lparam);
// Sprawdzamy: Czy to rysowanie dotyczy TEGO konkretnego ListBoxa?
if dis^.hwndItem = my_listbox_handle then
begin
draw_listbox_item(lparam);
Result := 1; // Zgłaszamy: Obsłużono!
Exit;
end;
end;
// Wyłapujemy WM_DELETEITEM (Sprzątanie przy usuwaniu elementu)
WM_DELETEITEM:
begin
// Jeśli to nasz ListBox, zwracamy True
if PDeleteItemStruct(lparam)^.hwndItem = my_listbox_handle then
begin
Result := 1;
Exit;
end;
end;
// Opcjonalnie: Zmiana koloru suwaka lub tła (WM_CTLCOLORLISTBOX)
WM_CTLCOLORLISTBOX:
begin
if lparam = my_listbox_handle then
begin
// Tutaj można zwrócić pędzel tła, jeśli draw_listbox_item nie wystarcza
// Ale przy OwnerDraw zazwyczaj nie jest to konieczne.
end;
end;
end;
// Wszystkie inne komunikaty puszczamy dalej do Rodzica
Result := DefSubclassProc(hwnd, msg, wparam, lparam);
end;
// --- 2. PROCEDURA LISTBOXA (Własne zachowanie kontrolki) ---
function listbox_proc(hwnd: HWND; msg: UINT; w_param: WPARAM; l_param: LPARAM;
u_id_subclass: UINT_PTR; dw_ref_data: DWORD_PTR): LRESULT; stdcall;
var
dc: HDC;
r: TRect;
brush: HBRUSH;
bg_color, prop_val: DWORD;
parent_handle: HWND;
my_listbox_handle: HWND;
begin
// Rzutujemy bezpiecznie: DWORD_PTR -> THandle -> HWND
my_listbox_handle := THandle(dw_ref_data);
case msg of
WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_KEYDOWN, WM_KEYUP:
begin
Result := DefSubclassProc(hwnd, msg, w_param, l_param);
draw_border(hwnd);
Exit;
end;
WM_ERASEBKGND:
begin
dc := HDC(w_param);
GetClientRect(hwnd, r);
prop_val := DWORD(GetProp(hwnd, AV_LIST_BG_COLOR));
if prop_val <> 0 then bg_color := prop_val - 1 else bg_color := $121212;
brush := CreateSolidBrush(bg_color);
FillRect(dc, r, brush);
DeleteObject(brush);
Result := 1;
Exit;
end;
WM_NCCALCSIZE:
begin
if w_param <> 0 then
begin
with PRect(l_param)^ do
begin
Left := Left + 1;
Top := Top + 1;
Right := Right - 1;
Bottom := Bottom - 1;
end;
Result := 0;
Exit;
end;
end;
WM_NCPAINT, WM_SETFOCUS, WM_KILLFOCUS:
begin
Result := DefSubclassProc(hwnd, msg, w_param, l_param);
draw_border(hwnd);
Exit;
end;
// --- SPRZĄTANIE ---
WM_NCDESTROY:
begin
// 1. Odpinamy procedurę LISTBOXA
RemoveWindowSubclass(hwnd, SUBCLASSPROC(@listbox_proc), u_id_subclass);
// 2. WAŻNE: Odpinamy procedurę RODZICA!
// Musimy pobrać rodzica, żeby wiedzieć z kogo zdjąć hooka.
parent_handle := GetParent(hwnd);
if parent_handle <> 0 then
begin
// Jako ID podajemy uchwyt listboxa (hwnd), tak jak przy tworzeniu
RemoveWindowSubclass(parent_handle, SUBCLASSPROC(@listbox_parent_proc), UINT_PTR(hwnd));
end;
// 3. Usuwamy właściwości
RemoveProp(hwnd, AV_LIST_BG_COLOR);
RemoveProp(hwnd, AV_LIST_TEXT_COLOR);
RemoveProp(hwnd, AV_LIST_SEL_BG);
RemoveProp(hwnd, AV_LIST_SEL_TEXT);
end;
end;
Result := DefSubclassProc(hwnd, msg, w_param, l_param);
if msg = WM_PAINT then
draw_border(hwnd);
end;
// --- TWORZENIE (Instalacja obu hooków) ---
function create_listbox(Parent: HWND; X, Y, Width, Height: Integer): HWND;
begin
// LBS_OWNERDRAWFIXED: My rysujemy, ale każdy wiersz ma stałą wysokość
// LBS_HASSTRINGS: ListBox przechowuje tekst (inaczej musiałbyś zarządzać pamięcią)
// LBS_NOINTEGRALHEIGHT: ListBox może mieć dowolną wysokość (nie ucina do pełnego wiersza)
Result := CreateWindowExW(
0,
'LISTBOX', nil,
WS_VISIBLE or
WS_CHILD or
WS_VSCROLL or
WS_TABSTOP or
LBS_NOTIFY or
LBS_OWNERDRAWFIXED or
LBS_HASSTRINGS or
LBS_NOINTEGRALHEIGHT or
WS_BORDER,
X, Y, Width, Height, Parent, 0, GetModuleHandle(nil), nil
);
if Result <> 0 then
begin
// Ustawiamy wysokość wiersza (np. 24 piksele - wygodne dla palca/myszy)
SendMessage(Result, LB_SETITEMHEIGHT, 0, 14);
// Domyślne kolory Avocado (Ciemne tło, Pomarańczowe zaznaczenie)
set_listbox_colors(Result, $FFFFFF, $121212, $000000, $00FF965F);
// Kolor czcionki tla Kolor tekstu
//set_listbox_colors(Result, $00FF6843, $121212, $00FF6843, $2D2D2D);
end;
end;
procedure list_add(h_list: HWND; const text: string);
begin
SendMessageW(h_list, LB_ADDSTRING, 0, LPARAM(PWideChar(UnicodeString(text))));
end;
procedure list_insert(h_list: HWND; index: Integer; const text: string);
begin
SendMessageW(h_list, LB_INSERTSTRING, index, LPARAM(PWideChar(UnicodeString(text))));
end;
function list_delete(h_list: HWND; index: Integer): Integer;
begin
Result := SendMessageW(h_list, LB_DELETESTRING, index, 0);
end;
procedure list_delete_selected(h_list: HWND);
var
idx: Integer;
begin
idx := SendMessageW(h_list, LB_GETCURSEL, 0, 0);
if idx <> LB_ERR then
SendMessageW(h_list, LB_DELETESTRING, idx, 0);
end;
procedure set_listbox_selection_color(Handle: HWND; TextColor, BgColor: DWORD);
begin
if Handle = 0 then Exit;
// Ustawiamy właściwości, które odczytuje avocado_draw_listbox_item w WM_DRAWITEM
SetPropW(Handle, AV_PROP_SEL_TXT_COLOR, THandle(TextColor));
SetPropW(Handle, AV_PROP_SEL_BG_COLOR, THandle(BgColor));
// Wymuszamy przerysowanie
InvalidateRect(Handle, nil, True);
end;
procedure list_set_data(h_list: HWND; index: Integer; value: Integer);
begin
SendMessageW(h_list, LB_SETITEMDATA, index, value);
end;
function list_get_data(h_list: HWND; index: Integer): Integer;
begin
Result := SendMessageW(h_list, LB_GETITEMDATA, index, 0);
end;
function list_get_selected_data(h_list: HWND): Integer;
var
idx: Integer;
begin
idx := SendMessageW(h_list, LB_GETCURSEL, 0, 0);
if idx <> LB_ERR then
Result := list_get_data(h_list, idx)
else
Result := 0;
end;
procedure set_listbox_colors(Handle: HWND; TextCol, BgCol, SelTextCol,
SelBgCol: DWORD);
begin
// Zapisujemy wszystkie 4 kolory we właściwościach
SetPropW(Handle, AV_PROP_TEXT_COLOR, THandle(TextCol));
SetPropW(Handle, AV_PROP_BG_COLOR, THandle(BgCol));
SetPropW(Handle, AV_PROP_SEL_TXT_COLOR, THandle(SelTextCol));
SetPropW(Handle, AV_PROP_SEL_BG_COLOR, THandle(SelBgCol));
InvalidateRect(Handle, nil, True);
end;
procedure list_begin_update(h_list: HWND);
begin
SendMessageW(h_list, WM_SETREDRAW, 0, 0);
end;
procedure list_end_update(h_list: HWND);
begin
SendMessageW(h_list, WM_SETREDRAW, 1, 0);
RedrawWindow(h_list, nil, 0, RDW_ERASE or RDW_FRAME or RDW_INVALIDATE or RDW_ALLCHILDREN);
end;
function list_find(h_list: HWND; const text: string): Integer;
begin
Result := SendMessageW(h_list, LB_FINDSTRINGEXACT, -1, LPARAM(PWideChar(UnicodeString(text))));
end;
function list_count(h_list: HWND): Integer;
begin
Result := SendMessageW(h_list, LB_GETCOUNT, 0, 0);
end;
procedure list_clear(h_list: HWND);
begin
SendMessageW(h_list, LB_RESETCONTENT, 0, 0);
end;
function list_get_index(h_list: HWND): Integer;
begin
Result := SendMessageW(h_list, LB_GETCURSEL, 0, 0);
end;
function list_get_text(h_list: HWND): string;
var
idx, len: Integer;
buffer: array of WideChar;
begin
Result := '';
idx := SendMessageW(h_list, LB_GETCURSEL, 0, 0);
if idx <> LB_ERR then
begin
len := SendMessageW(h_list, LB_GETTEXTLEN, idx, 0);
if len > 0 then
begin
SetLength(buffer, len + 1);
SendMessageW(h_list, LB_GETTEXT, idx, LPARAM(@buffer[0]));
Result := WideCharToString(@buffer[0]);
end;
end;
end;
procedure draw_listbox_item(mylparam: LPARAM);
var
DrawStruct: PDrawItemStruct;
DC: HDC;
R: TRect;
Idx: Integer;
IsSelected: Boolean;
TextCol, BgCol: DWORD;
Brush: HBRUSH;
Buffer: array[0..255] of WideChar;
begin
DrawStruct := PDrawItemStruct(mylparam);
DC := DrawStruct^.hDC;
R := DrawStruct^.rcItem;
Idx := DrawStruct^.itemID;
// 1. Obsługa pustej listy (gdy focus jest, ale nie ma elementów)
if Idx = -1 then
begin
if (DrawStruct^.itemState and ODS_FOCUS) <> 0 then
DrawFocusRect(DC, R);
Exit;
end;
// 2. Sprawdzamy, czy element jest zaznaczony
IsSelected := (DrawStruct^.itemState and ODS_SELECTED) <> 0;
// 3. Pobieramy uchwyt ListBoxa (z DrawStruct)
// i decydujemy o kolorach na podstawie stanu
if IsSelected then
begin
BgCol := DWORD(GetPropW(DrawStruct^.hwndItem, AV_PROP_SEL_BG_COLOR));
TextCol := DWORD(GetPropW(DrawStruct^.hwndItem, AV_PROP_SEL_TXT_COLOR));
end
else
begin
BgCol := DWORD(GetPropW(DrawStruct^.hwndItem, AV_PROP_BG_COLOR));
TextCol := DWORD(GetPropW(DrawStruct^.hwndItem, AV_PROP_TEXT_COLOR));
end;
// Fallback, jeśli kolory nie zostały ustawione (żeby nie było czarno na czarnym)
if BgCol = 0 then BgCol := GetSysColor(COLOR_WINDOW);
if (TextCol = 0) and (BgCol = 0) then TextCol := GetSysColor(COLOR_WINDOWTEXT);
// 4. RYSOWANIE TŁA (Wiersza)
Brush := CreateSolidBrush(BgCol);
FillRect(DC, R, Brush);
DeleteObject(Brush);
// 5. RYSOWANIE TEKSTU
// Pobieramy tekst z ListBoxa
SendMessageW(DrawStruct^.hwndItem, LB_GETTEXT, Idx, LPARAM(@Buffer));
SetBkMode(DC, TRANSPARENT);
SetTextColor(DC, TextCol);
// Margines tekstu (np. 5px od lewej)
R.Left := R.Left + 5;
// Rysujemy tekst wyśrodkowany w pionie
DrawTextW(DC, Buffer, -1, R, DT_LEFT or DT_VCENTER or DT_SINGLELINE);
// 6. RYSOWANIE RAMKI FOCUSU (Opcjonalne - dla klawiatury)
// Jeśli chcesz "czysty" wygląd jak w nowoczesnych apkach, możesz to zakomentować.
{
if (DrawStruct^.itemState and ODS_FOCUS) <> 0 then
DrawFocusRect(DC, DrawStruct^.rcItem);
}
end;
end.