ui.c (14070B)
1 #define _DEFAULT_SOURCE 2 3 #include <dirent.h> 4 #include "fileops.h" 5 #include "animation.h" 6 #include "ui.h" 7 #include "spritesheet.h" 8 9 //@todo might be rubbish to keep important stuff in a pile of static globals 10 static int cols = 1; 11 static UIWindow *modal_win = NULL; 12 static UIImageDisplay *img_disp = NULL; 13 static UILabel *lbl_header = NULL; 14 static Animation *img = NULL; 15 static char *current_path = NULL; 16 17 static int FilelistFilter(const struct dirent *f) { 18 if (strncmp(f->d_name, ".", 255)==0) return 0; 19 if (f->d_type==DT_DIR) return 1; 20 //@todo not very efficient but dirent doesn't have the length :c 21 size_t len = strlen(f->d_name); 22 // checking for the ".webp" at the very end 23 if (len<6) return 0; 24 return strncmp(f->d_name+len-5, ".webp", 255)==0; 25 } 26 27 static int FilelistCompare(const struct dirent **a, const struct dirent **b) { 28 if ((*a)->d_type==DT_DIR && (*b)->d_type==DT_DIR) { 29 if (strncmp((*a)->d_name, "..", 255)==0) return -1; 30 else if (strncmp((*b)->d_name, "..", 255)==0) return 1; 31 else return alphasort(a, b); 32 } 33 if ((*a)->d_type==DT_DIR) return -1; 34 if ((*b)->d_type==DT_DIR) return 1; 35 return alphasort(a, b); 36 } 37 38 UIWindow * MainWindowCreate(const char *wname, int w, int h) { 39 //UIFont *font = UIFontActivate(UIFontCreate("../font.ttf", 19)); 40 //assert(font!=NULL); 41 UIWindow *win = UIWindowCreate(0, 0, wname, w, h); 42 win->e.messageUser = WinMainEvent; 43 UIPanel *panelv = UIPanelCreate(&win->e, UI_PANEL_COLOR_2|UI_PANEL_MEDIUM_SPACING); 44 // label with filename, buttons open/save/exit 45 UIPanel *panelh_top = UIPanelCreate(&panelv->e, UI_PANEL_COLOR_2|UI_PANEL_MEDIUM_SPACING|UI_PANEL_HORIZONTAL|UI_ELEMENT_H_FILL); 46 UIButton *btnopen = UIButtonCreate(&panelh_top->e, 0, "Open", -1); 47 btnopen->e.messageUser = ButtonOpenEvent; 48 UIButton *btnsave = UIButtonCreate(&panelh_top->e, 0, "Save", -1); 49 btnsave->e.messageUser = ButtonSaveEvent; 50 btnsave->e.cp = win; 51 UIButton *btn_exit = UIButtonCreate(&panelh_top->e, 0, "Exit", -1); 52 btn_exit->e.messageUser = ButtonCloseEvent; 53 lbl_header = UILabelCreate(&panelh_top->e, 0, "webp to spritesheet converter", -1); 54 55 // scroll with dimensions label 56 UIPanel *panelh_bottom = UIPanelCreate(&panelv->e, UI_PANEL_COLOR_2|UI_PANEL_SMALL_SPACING|UI_PANEL_HORIZONTAL|UI_PANEL_EXPAND|UI_ELEMENT_H_FILL); 57 UILabel *label = UILabelCreate(&panelh_bottom->e, 0, "Size: 0x0px", -1); 58 UIButton *btnminus = UIButtonCreate(&panelh_bottom->e, 0, "-", -1); 59 UIButton *btnplus = UIButtonCreate(&panelh_bottom->e, 0, "+", -1); 60 UISlider *slider = UISliderCreate(&panelh_bottom->e, UI_ELEMENT_H_FILL); 61 62 btnminus->e.messageUser = ButtonMinusEvent; 63 btnminus->e.cp = slider; 64 btnplus->e.messageUser = ButtonPlusEvent; 65 btnplus->e.cp = slider; 66 67 img_disp = UIImageDisplayCreate(&panelv->e, UI_ELEMENT_V_FILL|UI_ELEMENT_H_FILL|UI_IMAGE_DISPLAY_INTERACTIVE|_UI_IMAGE_DISPLAY_ZOOM_FIT, NULL, 0, 0, 0); 68 img_disp->e.cp = label; 69 slider->e.messageUser = SliderEvent; 70 slider->e.cp = img_disp; 71 return win; 72 } 73 74 int WinModalEvent(UIElement *element, UIMessage msg, int di, void *dp) { 75 if (msg==UI_MSG_WINDOW_CLOSE) { 76 UIElementDestroy(element); 77 return 1; 78 } 79 if (msg==UI_MSG_DESTROY) { 80 assert(element==(UIElement *)modal_win); 81 modal_win = NULL; 82 } 83 return 0; 84 } 85 86 int ButtonMinusEvent(UIElement *element, UIMessage msg, int di, void *dp) { 87 if (modal_win!=NULL) return 1; 88 if (msg==UI_MSG_CLICKED) { 89 if (img==NULL) return 1; 90 UISlider *slider = (UISlider *)element->cp; 91 float step = 1.0f / (float)img->frame_count; 92 slider->position -= step; 93 cols--; 94 if (slider->position<0) slider->position=0; 95 if (cols<1) cols=1; 96 UIElementRefresh(&slider->e); 97 PreviewUpdate(img, img_disp); 98 } 99 return 0; 100 } 101 int ButtonPlusEvent(UIElement *element, UIMessage msg, int di, void *dp) { 102 if (modal_win!=NULL) return 1; 103 if (msg==UI_MSG_CLICKED) { 104 if (img==NULL) return 1; 105 UISlider *slider = (UISlider *)element->cp; 106 float step = 1.0f / (float)img->frame_count; 107 slider->position += step; 108 cols++; 109 if (slider->position>1) slider->position=1; 110 if (cols>img->frame_count) cols=img->frame_count; 111 UIElementRefresh(&slider->e); 112 PreviewUpdate(img, img_disp); 113 } 114 return 0; 115 } 116 117 int ButtonDialogSaveEvent(UIElement *element, UIMessage msg, int di, void *dp) { 118 if (msg==UI_MSG_CLICKED) { 119 // get values of path and filename inputs 120 UITextbox *path_input = (UITextbox *)element->parent->children[0]; 121 UITextbox *filename_input = (UITextbox *)element->parent->children[1]; 122 if (path_input->bytes<=0 || filename_input->bytes<=0) return 1; 123 // printf("path_input: %p\nfilename_input: %p\n", path_input, filename_input); 124 // that printf might work incorrectly because path_input->string might not contain valid C string with \0 at the end 125 // printf("path_input: %s(%d)\nfilename_input: %s(%d)\n", path_input->string, path_input->bytes, filename_input->string, filename_input->bytes); 126 int path_len = path_input->bytes + filename_input->bytes + 2; // DIR_SEPARATOR and '\0' 127 // printf("path_len: %d\n", path_len); 128 char out_name[path_len]; 129 out_name[path_input->bytes] = DIR_SEPARATOR; 130 out_name[path_len-1] = '\0'; 131 memcpy(out_name, path_input->string, path_input->bytes); 132 memcpy(out_name+path_input->bytes+1, filename_input->string, filename_input->bytes); 133 // printf("strlen(out_name): %d\n", strlen(out_name)); 134 // printf("out_name: %s\n", out_name); 135 WebpWrite(out_name, img, cols); 136 // printf("save dialog window close\n"); 137 assert(element->window==modal_win); 138 UIElementDestroy(&element->window->e); 139 } 140 return 0; 141 } 142 143 int ButtonDialogOpenEvent(UIElement *element, UIMessage msg, int di, void *dp) { 144 if (msg==UI_MSG_CLICKED) { 145 // printf("open dialog window close\n"); 146 UITextbox *path_input = (UITextbox *)element->parent->children[0]; 147 UITextbox *filename_input = (UITextbox *)element->parent->children[1]; 148 // printf("path_input: %p(%d)\nfilename_input: %p(%d)\n", path_input, path_input->bytes, filename_input, filename_input->bytes); 149 // printf("path_input: %s(%d)\nfilename_input: %s(%d)\n", path_input->string, strlen(path_input->string), filename_input->string, strlen(filename_input->string)); 150 151 if (path_input->bytes<=0 || filename_input->bytes<=0) return 1; 152 153 UIElementDestroy(&element->window->e); 154 155 int path_len = path_input->bytes + filename_input->bytes + 2; // DIR_SEPARATOR and '\0' 156 char filepath[path_len]; 157 filepath[path_input->bytes] = DIR_SEPARATOR; 158 filepath[path_len-1] = '\0'; 159 memcpy(filepath, path_input->string, path_input->bytes); 160 memcpy(filepath+path_input->bytes+1, filename_input->string, filename_input->bytes); 161 162 if (img != NULL) { 163 ImageUnload(&img); 164 assert(img==NULL); 165 } 166 img = ImageLoad(filepath); 167 if (img==NULL) return 1; 168 UILabelSetContent(lbl_header, img->path, -1); 169 PreviewUpdate(img, img_disp); 170 } 171 return 0; 172 } 173 174 int TableEvent(UIElement *element, UIMessage msg, int di, void *dp) { 175 static int selected = -1; 176 static struct dirent **filelist = NULL; 177 UITable *table = (UITable *)element; 178 if (filelist==NULL) { 179 table->itemCount = scandir(element->cp, &filelist, FilelistFilter, FilelistCompare); 180 // printf("populated dir %s with %d items\n", dir_name, table->itemCount); 181 } 182 if (msg==UI_MSG_TABLE_GET_ITEM) { 183 UITableGetItem *m = (UITableGetItem *)dp; 184 m->isSelected = selected==m->index; 185 bool is_dir = filelist[m->index]->d_type==DT_DIR; 186 //printf("render %s (%d)\n", filelist[m->index]->d_name, m->bufferBytes); 187 int ret = snprintf(m->buffer, m->bufferBytes, is_dir?"[%s]":"%s", filelist[m->index]->d_name); 188 //printf("%s - %d (%d)\n", filelist[m->index]->d_name, ret, m->bufferBytes); 189 return ret; 190 } else if (msg==UI_MSG_CLICKED) { 191 int hit = UITableHitTest(table, element->window->cursorX, element->window->cursorY); 192 if (hit!=-1 && selected==hit) { 193 if (filelist[hit]->d_type==DT_DIR) { 194 char newpath[PATH_MAX]; 195 bzero(newpath, PATH_MAX); 196 strcpy(newpath, element->cp); 197 strcat(newpath, "/"); 198 strcat(newpath, filelist[hit]->d_name); 199 free(element->cp); 200 element->cp = realpath(newpath, NULL); 201 current_path = element->cp; 202 //free(filelist); 203 filelist = NULL; 204 selected = -1; 205 //@todo duplicated code 206 UIPanel *panel_out = (UIPanel *)element->parent; 207 UILabel *label = (UILabel *)panel_out->e.children[0]; 208 UIPanel *panel_top = (UIPanel *)panel_out->e.children[1]; 209 UITextbox *path_input = (UITextbox *)panel_top->e.children[0]; 210 UITextboxClear(path_input, false); 211 UITextboxReplace(path_input, (char *)element->cp, -1, false); 212 UIElementRepaint(&path_input->e, NULL); 213 UITableEnsureVisible(table, -1); 214 } 215 } else { 216 selected = hit; 217 if (!UITableEnsureVisible(table, selected)) { 218 UIElementRepaint(element, NULL); 219 } 220 //@todo remove copypasta 221 if (hit!=-1 && filelist[hit]->d_type!=DT_DIR) { 222 UIPanel *panel_out = (UIPanel *)element->parent; 223 UILabel *label = (UILabel *)panel_out->e.children[0]; 224 UIPanel *panel_top = (UIPanel *)panel_out->e.children[1]; 225 UITextbox *path_input = (UITextbox *)panel_top->e.children[0]; 226 UITextbox *file_input = (UITextbox *)panel_top->e.children[1]; 227 UITextboxClear(path_input, false); 228 UITextboxClear(file_input, false); 229 UITextboxReplace(path_input, (char *)element->cp, -1, false); 230 UITextboxReplace(file_input, filelist[hit]->d_name, -1, false); 231 UIElementRepaint(&path_input->e, NULL); 232 UIElementRepaint(&file_input->e, NULL); 233 } 234 } 235 } else if (msg==UI_MSG_DESTROY) { 236 free(filelist); 237 filelist = NULL; 238 selected = -1; 239 UIElement *el = table->e.children[0]; 240 printf("child %p\n", el); 241 if (el!=NULL) { 242 printf("child %p\n", el); 243 } 244 } else if (msg==UI_MSG_UPDATE) { 245 UITableResizeColumns(table); 246 } 247 return 0; 248 } 249 250 int ButtonCloseEvent(UIElement *element, UIMessage msg, int di, void *dp) { 251 if (modal_win!=NULL) return 1; 252 if (msg==UI_MSG_CLICKED) { 253 UIElementDestroy(&element->window->e); 254 } 255 return 0; 256 } 257 258 void ShowModalWindow(UIWindow *parent, char *def_dir, const char *def_file, CallbackFn cb) { 259 assert(def_dir!=NULL); 260 if (modal_win==NULL) { 261 // printf("create modal window\n"); 262 int w = UIElementMessage(&parent->e, UI_MSG_GET_WIDTH, 0, 0); 263 int h = UIElementMessage(&parent->e, UI_MSG_GET_HEIGHT, 0, 0); 264 modal_win = UIWindowCreate(parent, UI_WINDOW_DIALOG, def_file?"Save File":"Open File", w, h); 265 modal_win->e.messageUser = WinModalEvent; 266 UIPanel *panel_out = UIPanelCreate(&modal_win->e, UI_PANEL_COLOR_2|UI_PANEL_EXPAND); 267 UILabel *lbl_title = UILabelCreate(&panel_out->e, 0, def_file?"Save File":"Open File", -1); 268 UIPanel *panel_top = UIPanelCreate(&panel_out->e, UI_PANEL_COLOR_2|UI_PANEL_MEDIUM_SPACING|UI_PANEL_HORIZONTAL); 269 UITextbox *path_input = UITextboxCreate(&panel_top->e, UI_ELEMENT_DISABLED|UI_ELEMENT_H_FILL); 270 UITextboxReplace(path_input, def_dir, -1, false); 271 UITextbox *filename_input = UITextboxCreate(&panel_top->e, UI_ELEMENT_TAB_STOP|UI_ELEMENT_H_FILL); 272 273 //printf("path_input: %p (%s)\nfilename_input: %p (%s)\n", path_input, def_dir, filename_input, def_file); 274 if (def_file!=NULL) { 275 UITextboxReplace(filename_input, def_file, -1, false); 276 } 277 278 UIButton *btn_save = UIButtonCreate(&panel_top->e, UI_ELEMENT_TAB_STOP, def_file?"Save":"Open", -1); 279 btn_save->e.messageUser = cb; 280 281 UIButton *btn_cancel = UIButtonCreate(&panel_top->e, UI_ELEMENT_TAB_STOP, "Cancel", -1); 282 btn_cancel->e.messageUser = ButtonCloseEvent; 283 284 UITable *table = UITableCreate(&panel_out->e, UI_ELEMENT_V_FILL, "Directory"); 285 table->itemCount = 1; // at least '..' element 286 table->e.cp = def_dir; 287 table->e.messageUser = TableEvent; 288 UITableResizeColumns(table); 289 } else { 290 UIElementMessage(&modal_win->e, UI_MSG_WINDOW_ACTIVATE, 0, 0); 291 puts("focus!"); 292 } 293 } 294 295 int ButtonSaveEvent(UIElement *element, UIMessage msg, int di, void *dp) { 296 if (modal_win!=NULL) return 1; 297 if (msg==UI_MSG_CLICKED && img!=NULL) { 298 // printf("save button clicked\n"); 299 char fname[strlen(img->filename)+7]; 300 int n = snprintf(fname, NAME_MAX, "atlas_%s", img->filename); 301 assert(n>0); 302 ShowModalWindow(element->window, img->dirname, fname, ButtonDialogSaveEvent); 303 } 304 return 0; 305 } 306 307 int ButtonOpenEvent(UIElement *element, UIMessage msg, int di, void *dp) { 308 if (modal_win!=NULL) return 1; 309 if (msg==UI_MSG_CLICKED) { 310 char *dir = current_path==NULL ? strndup(getenv("HOME"), strlen(getenv("HOME"))) : current_path; 311 // printf("open button clicked\n"); 312 ShowModalWindow(element->window, dir, NULL, ButtonDialogOpenEvent); 313 } 314 return 0; 315 } 316 317 int SliderEvent(UIElement *element, UIMessage msg, int di, void *dp) { 318 if (modal_win!=NULL) return 1; 319 if (msg==UI_MSG_VALUE_CHANGED) { 320 if (img==NULL) return 1; 321 322 float slider_pos = ((UISlider *)element)->position; 323 float step = 1.0f / (float)img->frame_count; 324 int new_cols = (int)(slider_pos / step); 325 if (new_cols>0 && cols!=new_cols) { 326 //printf("new_cols: %d\n", new_cols); 327 cols = new_cols; 328 PreviewUpdate(img, element->cp); 329 } 330 } 331 332 return 0; 333 } 334 335 int WinMainEvent(UIElement *element, UIMessage msg, int di, void *dp) { 336 if (msg==UI_MSG_DESTROY) { 337 free(img); 338 exit(0); 339 } 340 if (modal_win!=NULL) { 341 //UIElementMessage(&modal_win->e, UI_MSG_WINDOW_ACTIVATE, 0, 0); 342 //UIElementFocus((UIElement *)modal_win); 343 return 1; 344 } 345 return 0; 346 } 347 348 void PreviewUpdate(Animation *img, UIImageDisplay *img_disp) { 349 // gen new spritesheet and refresh the preview 350 Spritesheet ss = SpritesheetGen(img, cols); 351 //printf("spritesheet width: %d, height: %d, stride: %d, len: %zu\n", ss.width, ss.height, ss.stride, ss.len); 352 353 uint32_t *frame0 = calloc(ss.width*ss.height, sizeof(uint32_t)); 354 assert(frame0!=NULL); 355 356 for (uint32_t i=0; i<ss.width*ss.height; ++i) { 357 frame0[i] = UI_COLOR_FROM_RGBA(ss.data[i*4+0], ss.data[i*4+1], ss.data[i*4+2], ss.data[i*4+3]); 358 } 359 UIImageDisplaySetContent(img_disp, frame0, ss.width, ss.height, ss.stride); 360 UIElementRefresh(img_disp->e.parent); 361 UIElementRefresh(&img_disp->e); 362 363 free(frame0); 364 free(ss.data); 365 366 UILabel *label = (UILabel *)img_disp->e.cp; 367 char label_text[256] = {0}; 368 snprintf((char * restrict)&label_text, 255, "Size: %4dx%4dpx", ss.width, ss.height); 369 UILabelSetContent(label, label_text, -1); 370 UIElementRefresh(label->e.parent); 371 }