1 module unde.guitk.list;
2 
3 import derelict.sdl2.sdl;
4 import derelict.sdl2.ttf;
5 import derelict.sdl2.image;
6 
7 import std.stdio;
8 import std.math;
9 import std..string;
10 
11 import unde.global_state;
12 import unde.guitk.lib;
13 import unde.keybar.lib;
14 import unde.lib;
15 import unde.tick;
16 
17 version(Windows)
18 {
19 import berkeleydb.all: ssize_t;
20 }
21 
22 class List:UIEntry
23 {
24     private SDL_Rect _rect;
25     private UIPage _page;
26     private bool _focus;
27     string[] list;
28     private bool[ssize_t] _selected;
29     string filter;
30     private int x;
31     private int y;
32     private ssize_t pos;
33     private ssize_t mouse_pos;
34     private int fontsize;
35     private int line_height;
36     private SDL_Color color;
37     private bool wraplines;
38     private int multiselect;
39 
40     this(UIPage page, SDL_Rect rect, string[] list, int multiselect = 2,
41             SDL_Color color = SDL_Color(0xFF, 0xFF, 0xFF, 0xFF))
42     {
43         _page = page;
44         _rect = rect;
45         this.list = list;
46         fontsize = 9;
47         line_height = cast(int)(round(SQRT2^^fontsize)*1.2);
48         this.multiselect = multiselect;
49         this.color = color;
50     }
51 
52     @property ref bool[ssize_t] selected() {return _selected;}
53 
54     @property SDL_Rect rect() {return _rect;}
55 
56     void delegate (GlobalState gs) pre_draw;
57     
58     void on_draw(GlobalState gs)
59     {
60         if (pre_draw) pre_draw(gs);
61         /* Background */
62         auto r = SDL_RenderCopy(gs.renderer, gs.texture_gray, null, &_rect);
63         if (r < 0)
64         {
65             writefln( "List.on_draw(), 1: Error while render copy: %s",
66                     SDL_GetError().fromStringz() );
67         }
68 
69         if (y > 0)
70         {
71             int y_off = cast(int)(_rect.y + y);
72 
73             for (ssize_t i = pos-1; i >= 0; i--)
74             {
75                 if (filter > "" && list[i].indexOf(filter) < 0)
76                     continue;
77 
78                 auto rect = gs.text_viewer.font.get_size_of_line(list[i],
79                         fontsize,  wraplines ? _rect.w : 0, line_height, color);
80 
81                 pos = i;
82                 y -= rect.h;
83                 y_off -= rect.h;
84 
85                 if (y_off < _rect.y)
86                 {
87                     break;
88                 }
89             }
90         }
91 
92         int y_off = cast(int)(_rect.y + y);
93 
94         for (ssize_t i = pos; i < list.length; i++)
95         {
96             if (filter > "" && list[i].indexOf(filter) < 0)
97                 continue;
98 
99             ssize_t start_pos = -1;
100             ssize_t end_pos = -1;
101             if (i in _selected)
102             {
103                 start_pos = 0;
104                 end_pos = list[i].length-1;
105             }
106             auto tt = gs.text_viewer.font.get_line_from_cache(list[i], 
107                     fontsize, wraplines ? _rect.w : 0, line_height, color,
108                     null, start_pos, end_pos);
109             if (!tt && !tt.texture)
110             {
111                 throw new Exception("Can't create text_surface: "~
112                         TTF_GetError().fromStringz().idup());
113             }
114 
115             if (y_off + tt.h < _rect.y)
116             {
117                 pos = i+1;
118                 y += tt.h;
119                 y_off += tt.h;
120                 continue;
121             }
122 
123             SDL_RenderSetClipRect(gs.renderer, &_rect);
124 
125             SDL_Rect rect;
126             rect.x = _rect.x + x;
127             rect.y = y_off;
128             rect.w = tt.w;
129             rect.h = tt.h;
130 
131             if (gs.mouse_screen_x > rect.x && gs.mouse_screen_x < rect.x+rect.w &&
132                     gs.mouse_screen_y > rect.y && gs.mouse_screen_y < rect.y+rect.h)
133             {
134                 mouse_pos = i;
135             }
136 
137             r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect);
138             if (r < 0)
139             {
140                 writefln(
141                         "List.on_draw(), 2: Error while render copy: %s", 
142                         SDL_GetError().fromStringz() );
143             }
144 
145             SDL_RenderSetClipRect(gs.renderer, null);
146 
147             if (rect.y + rect.h > _rect.y + _rect.h)
148             {
149                 break;
150             }
151 
152             y_off += tt.h;
153         }
154     }
155 
156     void delegate (GlobalState gs, ssize_t pos) on_select;
157     void delegate (GlobalState gs, ssize_t pos) on_deselect;
158 
159     void process_event(GlobalState gs, SDL_Event event)
160     {
161         switch (event.type)
162         {
163             case SDL_MOUSEMOTION:
164                 if (gs.mouse_buttons & unDE_MouseButtons.Left)
165                 {
166                     if (!wraplines)
167                         x += event.motion.xrel;
168                     y += event.motion.yrel;
169                 }
170                 break;
171 
172             case SDL_MOUSEBUTTONDOWN:
173                 break;
174 
175             case SDL_MOUSEBUTTONUP:
176                 switch (event.button.button)
177                 {
178                     case SDL_BUTTON_LEFT:
179                         set_focus(gs);
180                         break;
181 
182                     case SDL_BUTTON_RIGHT:
183                         if (SDL_GetTicks() - gs.last_right_click < DOUBLE_DELAY)
184                         {
185                         }
186                         else
187                         {
188                             switch (multiselect)
189                             {
190                                 case 0:
191                                     break;
192 
193                                 case 1:
194                                     _selected.clear();
195                                     _selected[mouse_pos] = true;
196                                     if (on_select)
197                                         on_select(gs, mouse_pos);
198                                     break;
199 
200                                 case 2:
201                                     if (mouse_pos in _selected)
202                                     {
203                                         _selected.remove(mouse_pos);
204                                         if (on_deselect)
205                                             on_deselect(gs, mouse_pos);
206                                     }
207                                     else
208                                     {
209                                         _selected[mouse_pos] = true;
210                                         if (on_select)
211                                             on_select(gs, mouse_pos);
212                                     }
213                                     break;
214 
215                                 default:
216                                     assert(0);
217                             }
218                         }
219                         break;
220                     default:
221                         break;
222                 }
223                 break;
224 
225             case SDL_MOUSEWHEEL:
226                 break;
227             default:
228                 break;
229         }
230     }
231 
232     @property UIPage page() {return _page;}
233 
234     @property ref bool focus() {return _focus;}
235 
236     void on_set_focus(GlobalState gs)
237     {
238     }
239 
240     void on_unset_focus(GlobalState gs)
241     {
242     }
243 
244     private void
245     close_page(GlobalState gs)
246     {
247         _page.show = false;
248     }
249 
250     private void
251     change_wrap_mode(GlobalState gs)
252     {
253         wraplines = !wraplines;
254         if (wraplines)
255         {
256             x = 0;
257         }
258     }
259 
260     void set_keybar(GlobalState gs)
261     {
262         gs.keybar.handlers.clear();
263         gs.keybar.handlers_down.clear();
264         gs.keybar.handlers_double.clear();
265 
266         gs.keybar.handlers[SDL_SCANCODE_ESCAPE] = KeyHandler(&close_page, "Close layouts settings", "Esc");
267         gs.keybar.handlers[SDL_SCANCODE_W] = KeyHandler(&change_wrap_mode, "On/Off wrap lines", "Wrap");
268     }
269 }
270