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