emote2ss

Animated webp to spritesheets converting tool
git clone git://bsandro.tech/emote2ss
Log | Files | Refs | README | LICENSE

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 }