#include #include #include #include #define SBDF_USESDL #include "ui.h" #include "sbdf.h" #include "cdraw.h" #include "types.h" #include "action.h" #include "canvas.h" #include "tools.h" #include "debug.h" #include "palette.h" #define fpan main_ui->focus_panel #define MAX(a, b) (((a) > (b)) ? (a) : (b)); static const SDL_Rect ui_frame_rect[] = { {0, 0, 8, 8}, {16, 0, 8, 8}, {0, 16, 8, 8}, {16, 16, 8, 8}, {8, 0, 8, 8}, {8, 16, 8, 8}, {0, 8, 8, 8}, {16, 8, 8, 8}, }; static const SDL_Rect ui_buttons_rect[] = { {0, 24, 28, 28}, {0, 52, 28, 28}, {0, 80, 32, 32}, /* TODO: y -= 2 all the rects below */ {28, 26, 28, 28}, /* Pencil */ {28, 54, 28, 28}, /* Eraser */ {84, 26, 28, 28}, /* Fill Bucket */ {56, 26, 28, 28}, /* Zoom in */ {56, 54, 28, 28}, /* Zoom out */ {84, 54, 28, 28}, /* Clown */ {56, 82, 28, 28}, /* Color Picker */ {84, 82, 28, 28}, /* Undo */ {84, 110, 28, 28}, /* Redo */ }; static const SDL_Rect ui_timeline_rect[] = { {112, 0, 20, 20}, {112, 20, 20, 20}, }; struct UIPanel { uint8 type, redraw; SDL_Rect geom; SDL_Texture *pres; }; struct UIPanelCanvas { UIPanel head; /* Canvas *canv; */ }; struct UIPanelPalette { UIPanel head; }; struct UIPanelButtons { UIPanel head; }; struct UIPanelDivider { UIPanel head; UIPanel *x, *y; uint8 st; /* states: (0 bit - hor/vert, 1 bit - ra/lb) */ int fw, lw, of; }; struct UIPanelTimeline { UIPanel head; }; struct UI { SDL_Texture *theme; int w, h; UIPanel *focus_panel; UIPanelDivider p_divs[3]; UIPanelCanvas p_canvas; UIPanelPalette p_palette; UIPanelButtons p_buttons; UIPanelTimeline p_timeline; }; UI *main_ui; static UIPanel *ui_focus_panel(int, int); static void ui_focus_panel_change(int, int); /* Panel functions */ static uint8 ui_panel_init(UIPanel *, uint8); static uint8 ui_panel_resize(UIPanel *, int, int, uint, uint); static uint8 ui_panel_draw_frame(UIPanel *); static uint8 ui_panel_redraw(UIPanel *); static uint8 ui_panel_present(UIPanel *); /* Panel canvas functions */ static uint8 ui_panel_canvas_init(UIPanelCanvas *); static uint8 ui_panel_canvas_redraw(UIPanelCanvas *); /* Panel palette functions */ static uint8 ui_panel_palette_init(UIPanelPalette *); static uint8 ui_panel_palette_redraw(UIPanelPalette *); /* Panel buttons functions */ static uint8 ui_panel_buttons_init(UIPanelButtons *); static uint8 ui_panel_buttons_redraw(UIPanelButtons *); /* Panel timeline functions */ static uint8 ui_panel_timeline_init(UIPanelTimeline *); static uint8 ui_panel_timeline_redraw(UIPanelTimeline *); /* Panel divider functions */ static uint8 ui_panel_divider_init(UIPanelDivider *, int, int, int, uint8, UIPanel *, UIPanel *); static uint8 ui_panel_divider_resize(UIPanelDivider *, int, int, int, int); void ui_create(const char *path) { /* TODO: error checking */ int i; main_ui = malloc(sizeof *main_ui); main_ui->theme = NULL; main_ui->focus_panel = NULL; main_ui->w = main_ui->h = -1; if (ui_theme_load(path)) exit(1); SDL_SetTextureBlendMode(main_ui->theme, SDL_BLENDMODE_BLEND); ui_panel_canvas_init(&main_ui->p_canvas); ui_panel_palette_init(&main_ui->p_palette); ui_panel_buttons_init(&main_ui->p_buttons); ui_panel_timeline_init(&main_ui->p_timeline); /* ui_panel_divider_init(&main_ui->p_divs[0], 5, 10, 100, 1, &main_ui->p_canvas, &main_ui->p_timeline); */ ui_panel_divider_init(&main_ui->p_divs[0], 5, 5, 120, 0, &main_ui->p_divs[1], &main_ui->p_divs[2]); ui_panel_divider_init(&main_ui->p_divs[1], 0, 8, 22+31*((tool_array_size()+2)/3), 3, &main_ui->p_palette, &main_ui->p_buttons); ui_panel_divider_init(&main_ui->p_divs[2], 0, 5, 120, 3, &main_ui->p_canvas, &main_ui->p_timeline); ui_resize(); } void ui_destroy() { if (main_ui == NULL) return; if (main_ui->theme) SDL_DestroyTexture(main_ui->theme); free(main_ui); } uint8 ui_resize() { static int w, h; if (main_ui == NULL) return 1; SDL_GetRendererOutputSize(ren, &w, &h); if (w != main_ui->w || h != main_ui->h) { main_ui->w = w; main_ui->h = h; ui_panel_resize(&main_ui->p_divs[0], 0, 0, w, h); /* ui_panel_resize(&main_ui->p_palette.head, 5, 5, 120, main_ui->h-42-31*((tool_array_size()+2)/3)); */ /* ui_panel_resize(&main_ui->p_buttons.head, 5, main_ui->h-27-31*((tool_array_size()+2)/3), 120, 22+31*((tool_array_size()+2)/3)); */ /* ui_panel_resize(&main_ui->p_canvas.head, 130, 5, main_ui->w - 135, main_ui->h-85); */ /* ui_panel_resize(&main_ui->p_timeline.head, 130, main_ui->h-75, main_ui->w - 135,70); */ ui_redraw(); } return 0; } void ui_redraw() { main_ui->p_canvas.head.redraw = 1; main_ui->p_palette.head.redraw = 1; main_ui->p_buttons.head.redraw = 1; main_ui->p_timeline.head.redraw = 1; } void ui_redraw_panel(uint8 type) { switch (type) { case UI_PANELTYPE_PALETTE: main_ui->p_palette.head.redraw = 1; break; case UI_PANELTYPE_CANVAS: main_ui->p_canvas.head.redraw = 1; break; case UI_PANELTYPE_BUTTONS: main_ui->p_buttons.head.redraw = 1; break; case UI_PANELTYPE_TIMELINE: main_ui->p_timeline.head.redraw = 1; break; default: fprintf(stderr, "%s:%d:ui_redraw_panel: No implementation for %u\n", __FILE__, __LINE__, type); break; } } void ui_present() { if (main_ui->p_palette.head.redraw) ui_panel_redraw(&main_ui->p_palette.head); if (main_ui->p_canvas.head.redraw) ui_panel_redraw(&main_ui->p_canvas.head); if (main_ui->p_buttons.head.redraw) ui_panel_redraw(&main_ui->p_buttons.head); if (main_ui->p_timeline.head.redraw) ui_panel_redraw(&main_ui->p_timeline.head); ui_panel_present(&main_ui->p_palette.head); ui_panel_present(&main_ui->p_canvas.head); ui_panel_present(&main_ui->p_buttons.head); ui_panel_present(&main_ui->p_timeline.head); } void ui_mousel_down(int x, int y) { int col; Arg arg; ui_focus_panel_change(x, y); if (main_ui->focus_panel == NULL) return; switch (main_ui->focus_panel->type) { case UI_PANELTYPE_CANVAS: if (cur_canvas != NULL) { canvas_mousel_down(cur_canvas, x - fpan->geom.x - 8, y - fpan->geom.y - 8); fpan->redraw = 1; } break; case UI_PANELTYPE_PALETTE: if (cur_canvas == NULL) return; col = (x - fpan->geom.x - 8) / 26; if (col > 3) col = 3; col += (((y - fpan->geom.y - 8) / 26) << 2); if (col < ((Palette *)def_palette)->num && col >= 0) cur_canvas->cur_col = col; break; case UI_PANELTYPE_BUTTONS: /* TODO Better */ col = (x - fpan->geom.x - 15) / 30; if (col > 2) break; col += 3 * ((y - fpan->geom.y - 2) / 30); if (col >= tool_array_size() || col < 0) break; switch (tool_array[col]) { case TOOL_TYPE_ZOOMOUT: /* TODO: user functions must mostly be wrappers for internal fucntions */ arg.i = -1; user_canvas_zoom_change(&arg); break; case TOOL_TYPE_ZOOMIN: arg.i = 1; user_canvas_zoom_change(&arg); break; case TOOL_TYPE_CLOWN: fprintf(stdout, "You have been clowned 🤡\n"); break; case TOOL_TYPE_EMPTY: break; case TOOL_TYPE_UNDO: user_canvas_undo(NULL); break; case TOOL_TYPE_REDO: user_canvas_redo(NULL); break; default: tool_change(col); break; } break; case UI_PANELTYPE_TIMELINE: if (cur_canvas == NULL) return; x = x - fpan->geom.x - 8; y = y - fpan->geom.y - 8; y /= 22; if (y < 0 || y >= cur_canvas->layer_arr_cnt) break; if (x <= 22) { cur_canvas->layers[cur_canvas->layer_arr_cnt-1-y]->visible ^= 1; canvas_refresh(cur_canvas); ui_redraw_panel(UI_PANELTYPE_TIMELINE); ui_redraw_panel(UI_PANELTYPE_CANVAS); } else if (x <= 162) { y = cur_canvas->layer_arr_cnt-y-1; y -= (int)(cur_canvas->cur_layer); user_layer_chng(&y); } break; } } void ui_mousel_up(int x, int y) { ui_focus_panel_change(x, y); if (main_ui->focus_panel == NULL) return; switch (main_ui->focus_panel->type) { case UI_PANELTYPE_CANVAS: if (cur_canvas != NULL) canvas_mousel_up(cur_canvas); fpan->redraw = 1; break; } } void ui_mouse_move(int x, int y) { ui_focus_panel_change(x, y); if (main_ui->focus_panel == NULL) return; switch (main_ui->focus_panel->type) { case UI_PANELTYPE_CANVAS: if (cur_canvas != NULL) { canvas_mouse_move(cur_canvas, x - fpan->geom.x - 8, y - fpan->geom.y - 8); fpan->redraw = 1; } break; } } uint8 ui_theme_load(const char *path) { /* TODO: check path */ if (main_ui->theme != NULL) SDL_DestroyTexture(main_ui->theme); SDL_Surface *surf = IMG_Load(path); if (surf == NULL) return 1; main_ui->theme = SDL_CreateTextureFromSurface(ren, surf); SDL_FreeSurface(surf); if (main_ui->theme == NULL) return 1; return 0; } static UIPanel * ui_focus_panel(int x, int y) { SDL_Rect *r; if (main_ui == NULL) return NULL; r = &main_ui->p_palette.head.geom; if (r->x <= x && r->y <= y && x - r->x < r->w && y - r->y < r->h) return &main_ui->p_palette.head; r = &main_ui->p_buttons.head.geom; if (r->x <= x && r->y <= y && x - r->x < r->w && y - r->y < r->h) return &main_ui->p_buttons.head; r = &main_ui->p_canvas.head.geom; if (r->x <= x && r->y <= y && x - r->x < r->w && y - r->y < r->h) return &main_ui->p_canvas.head; r = &main_ui->p_timeline.head.geom; if (r->x <= x && r->y <= y && x - r->x < r->w && y - r->y < r->h) return &main_ui->p_timeline.head; return NULL; } static void ui_focus_panel_change(int x, int y) { UIPanel *u; u = ui_focus_panel(x, y); if (u == main_ui->focus_panel) return; if (main_ui->focus_panel != NULL) { switch (main_ui->focus_panel->type) { case UI_PANELTYPE_CANVAS: canvas_mousel_up(cur_canvas); main_ui->focus_panel = u; ui_redraw_panel(UI_PANELTYPE_CANVAS); break; default: break; } } main_ui->focus_panel = u; } /* Panel functions */ static uint8 ui_panel_init(UIPanel *p, uint8 type) { p->pres = NULL; p->type = type; p->redraw = 1; p->geom.w = p->geom.h = 0; return 0; } static uint8 ui_panel_resize(UIPanel *p, int x, int y, uint w, uint h) { if (p->type == UI_PANELTYPE_DIVIDER) return ui_panel_divider_resize((UIPanelDivider *)p, x, y, w, h); p->geom.x = x; p->geom.y = y; /* assert(w > 16 && h > 16); */ if (w < 16 || h < 16) return 1; if (p->geom.w == w && p->geom.h == h) return 0; p->geom.w = w; p->geom.h = h; if (p->pres != NULL) SDL_DestroyTexture(p->pres); p->pres = SDL_CreateTexture( ren, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h); SDL_SetTextureBlendMode(p->pres, SDL_BLENDMODE_BLEND); return 0; } static uint8 ui_panel_redraw(UIPanel *p) { /* TODO: something for paralel threads? */ /* TODO: Correct switch case intendation(not here) */ if (p->pres == NULL) return 1; p->redraw = 0; SDL_SetRenderTarget(ren, p->pres); SDL_SetRenderDrawColor(ren, INTTOCOLA(0)); SDL_RenderClear(ren); switch (p->type) { case UI_PANELTYPE_CANVAS: ui_panel_canvas_redraw((UIPanelCanvas *)p); break; case UI_PANELTYPE_PALETTE: ui_panel_palette_redraw((UIPanelPalette *)p); break; case UI_PANELTYPE_BUTTONS: ui_panel_buttons_redraw((UIPanelButtons *)p); break; case UI_PANELTYPE_TIMELINE: ui_panel_timeline_redraw((UIPanelTimeline *)p); break; default: fprintf(stderr, "%s:%d:ui_panel_redraw: No redraw fuction for %u\n", __FILE__, __LINE__, p->type); break; } SDL_SetRenderTarget(ren, NULL); return 0; } static uint8 ui_panel_draw_frame(UIPanel *p) { static SDL_Rect dest; dest.x = 0; dest.y = 0; dest.h = 8; dest.w = p->geom.w; SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[4], &dest); dest.y = p->geom.h-8; SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[5], &dest); dest.w = 8; dest.h = p->geom.h; dest.y = 0; SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[6], &dest); dest.x = p->geom.w-8; SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[7], &dest); dest.h = 8; SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[1], &dest); dest.x = 0; SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[0], &dest); dest.y = p->geom.h-8; SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[2], &dest); dest.x = p->geom.w-8; SDL_RenderCopy(ren, main_ui->theme, &ui_frame_rect[3], &dest); return 0; } static uint8 ui_panel_present(UIPanel *p) { return SDL_RenderCopy(ren, p->pres, NULL, &p->geom); } /* Panel canvas functions */ static uint8 ui_panel_canvas_init(UIPanelCanvas *p) { return ui_panel_init((UIPanel *)p, UI_PANELTYPE_CANVAS); } static uint8 ui_panel_canvas_redraw(UIPanelCanvas *p) { static SDL_Rect dest; dest.x = 8; dest.y = 8; dest.w = p->head.geom.w - 16; dest.h = p->head.geom.h - 16; SDL_RenderSetViewport(ren, &dest); SDL_SetRenderDrawColor(ren, INTTOCOLA(0x121212ff)); SDL_RenderClear(ren); if (cur_canvas != NULL) { SDL_RenderSetViewport(ren, NULL); canvas_redraw(cur_canvas, ren, mouseX - p->head.geom.x - 8, mouseY - p->head.geom.y - 8); SDL_SetRenderTarget(ren, p->head.pres); SDL_RenderSetViewport(ren, &dest); canvas_present(cur_canvas, ren); } SDL_RenderSetViewport(ren, NULL); ui_panel_draw_frame(&p->head); return 0; } /* Panel palette functions */ static uint8 ui_panel_palette_init(UIPanelPalette *p) { return ui_panel_init((UIPanel *)p, UI_PANELTYPE_PALETTE); } static uint8 ui_panel_palette_redraw(UIPanelPalette *p) { static SDL_Rect dest; Palette *pp; int i; pp = def_palette; dest.x = 8; dest.y = 8; dest.w = p->head.geom.w - 16; dest.h = p->head.geom.h - 16; SDL_RenderSetViewport(ren, &dest); SDL_SetRenderDrawColor(ren, INTTOCOLA(0x121212ff)); SDL_RenderClear(ren); SDL_SetRenderDrawColor(ren, INTTOCOLA(0x000000ff)); if (pp != NULL) { dest.x = dest.y = 0; dest.w = 120; if (pp->num > 3) { dest.h = 26 * (pp->num >> 2); SDL_RenderFillRect(ren, &dest); } if (pp->num % 4) { dest.w = 26 * (pp->num % 4); dest.h = 26; dest.y = 26 * (pp->num >> 2); SDL_RenderFillRect(ren, &dest); } dest.w = dest.h = 24; for (i = 0; i < pp->num; ++i) { dest.x = 1 + (i % 4) * 26; dest.y = 1 + (i / 4) * 26; SDL_SetRenderDrawColor(ren, pp->clist[i].r, pp->clist[i].g, pp->clist[i].b, 255); SDL_RenderFillRect(ren, &dest); } } SDL_RenderSetViewport(ren, NULL); ui_panel_draw_frame(&p->head); return 0; } /* Panel buttons functions */ static uint8 ui_panel_buttons_init(UIPanelButtons *p) { return ui_panel_init((UIPanel *)p, UI_PANELTYPE_BUTTONS); } static uint8 ui_panel_buttons_redraw(UIPanelButtons *p) { static SDL_Rect dest1; static SDL_Rect dest2; int i; dest1.x = 14; dest1.y = 0; dest1.w = p->head.geom.w - 28; dest1.h = p->head.geom.h; SDL_RenderSetViewport(ren, &dest1); SDL_SetRenderDrawColor(ren, INTTOCOL(back_c), 255); SDL_RenderClear(ren); dest1.w = 28; dest1.h = 28; dest2.w = 32; dest2.h = 32; for (i = 0; i < tool_array_size(); ++i) { if (tool_array[i] == TOOL_TYPE_EMPTY) continue; dest1.x = 2 + (i % 3) * 30; dest1.y = 2 + (i / 3) * 30; dest2.x = (i % 3) * 30; dest2.y = (i / 3) * 30; SDL_RenderCopy(ren, main_ui->theme, &ui_buttons_rect[2], &dest2); if (i == tool_cur) SDL_RenderCopy(ren, main_ui->theme, &ui_buttons_rect[0], &dest1); else SDL_RenderCopy(ren, main_ui->theme, &ui_buttons_rect[1], &dest1); SDL_RenderCopy(ren, main_ui->theme, &ui_buttons_rect[tool_array[i]+3], &dest1); } SDL_RenderSetViewport(ren, NULL); /* ui_panel_draw_frame(&p->head); */ return 0; } /* Panel timeline functions */ static uint8 ui_panel_timeline_init(UIPanelTimeline *p) { return ui_panel_init((UIPanel *)p, UI_PANELTYPE_TIMELINE); } static uint8 ui_panel_timeline_redraw(UIPanelTimeline *p) { SDL_Rect dest; int i; dest.x = 6; dest.y = 6; dest.w = p->head.geom.w - 12; dest.h = p->head.geom.h - 12; SDL_RenderSetViewport(ren, &dest); SDL_SetRenderDrawColor(ren, INTTOCOLA(0xc7cfddff)); SDL_RenderClear(ren); if (cur_canvas != NULL) { dest.x = 2; dest.h = 20; dest.w = 162; dest.y = (cur_canvas->layer_arr_cnt - 1 - cur_canvas->cur_layer) * 22 + 2; SDL_SetRenderDrawColor(ren, INTTOCOLA(0x657392ff)); SDL_RenderFillRect(ren, &dest); for (i = 0; i < cur_canvas->layer_arr_cnt; i++) { dest.y = 6 + (cur_canvas->layer_arr_cnt - 1 - i) * 22; dest.x = 6; dest.h = 24; dest.w = 166; SDL_RenderSetViewport(ren, &dest); SDL_SetRenderDrawColor(ren, INTTOCOLA(0x131313ff)); SDL_RenderDrawLine(ren, 2, 0, 21, 0); SDL_RenderDrawLine(ren, 2, 1, 21, 1); SDL_RenderDrawLine(ren, 0, 2, 0, 21); SDL_RenderDrawLine(ren, 1, 2, 1, 21); SDL_RenderDrawLine(ren, 2, 22, 21, 22); SDL_RenderDrawLine(ren, 2, 23, 21, 23); SDL_RenderDrawLine(ren, 22, 2, 22, 21); SDL_RenderDrawLine(ren, 23, 2, 23, 21); SDL_RenderDrawLine(ren, 24, 0, 163, 0); SDL_RenderDrawLine(ren, 24, 1, 163, 1); SDL_RenderDrawLine(ren, 24, 22, 163, 22); SDL_RenderDrawLine(ren, 24, 23, 163, 23); SDL_RenderDrawLine(ren, 164, 2, 164, 21); SDL_RenderDrawLine(ren, 165, 2, 165, 21); SDL_SetRenderDrawColor(ren, INTTOCOLA(0x000000ff)); dest.y = dest.x = 2; dest.w = dest.h = 20; if (cur_canvas->layers[i]->visible) SDL_RenderCopy(ren, main_ui->theme, &ui_timeline_rect[0], &dest); else SDL_RenderCopy(ren, main_ui->theme, &ui_timeline_rect[1], &dest); dest.y = 6 + (cur_canvas->layer_arr_cnt - 1 - i) * 22; dest.x = 30; dest.w = 140; dest.h = 24; SDL_RenderSetViewport(ren, &dest); SBDF_SDLPrint(ren, font, cur_canvas->layers[i]->name, 2, 5); } } SDL_RenderSetViewport(ren, NULL); ui_panel_draw_frame(&p->head); } /* Panel divider functions */ static uint8 ui_panel_divider_init(UIPanelDivider *p, int fw, int lw, int of, uint8 st, UIPanel *p1, UIPanel *p2) { ui_panel_init((UIPanel *)p, UI_PANELTYPE_DIVIDER); p->fw = fw; p->lw = lw; p->of = of; p->st = st; p->x = p1; p->y = p2; return 0; } static uint8 ui_panel_divider_resize(UIPanelDivider *p, int x, int y, int w, int h) { SDL_Rect g1, g2; p->head.geom.x = x; p->head.geom.y = y; p->head.geom.w = w; p->head.geom.h = h; if (p->st & 1) { /* Horizontal line */ g1.w = g2.w = MAX(0, p->head.geom.w - 2 * p->fw); if (p->st & 2) { g2.h = p->of; g1.h = MAX(0, p->head.geom.h - 2 * p->fw - p->of - p->lw); } else { g1.h = p->of; g2.h = MAX(0, p->head.geom.h - 2 * p->fw - p->of - p->lw); } g1.x = g2.x = x + p->fw; g1.y = y + p->fw; g2.y = y + p->fw + p->lw + g1.h; } else { /* Vertical line */ g1.h = g2.h = MAX(0, p->head.geom.h - 2 * p->fw); if (p->st & 2) { g2.w = p->of; g1.w = MAX(0, p->head.geom.w - 2 * p->fw - p->of - p->lw); } else { g1.w = p->of; g2.w = MAX(0, p->head.geom.w - 2 * p->fw - p->of - p->lw); } g1.y = g2.y = x + p->fw; g1.x = x + p->fw; g2.x = x + p->fw + p->lw + g1.w; } ui_panel_resize(p->x, g1.x, g1.y, g1.w, g1.h); ui_panel_resize(p->y, g2.x, g2.y, g2.w, g2.h); return 0; }