cepheid

An Aurora 4X clone
Log | Files | Refs | README

commit e49272570ff63ab0afbd4a74f3a97ab4f150fb07
parent c4b9af2f720b7261863f2d671b4ee24babcbec3c
Author: hhvn <dev@hhvn.uk>
Date:   Sat,  8 Oct 2022 16:17:45 +0100

Reorganize gui and views

Diffstat:
Asrc/gui.c | 313+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.c | 1-
Msrc/main.h | 23++++++++++-------------
Msrc/struct.h | 18+++++++++---------
Msrc/system.c | 9++-------
Msrc/ui.c | 101+++++--------------------------------------------------------------------------
Dsrc/ui/bodies.c | 182-------------------------------------------------------------------------------
Dsrc/ui/main.c | 293-------------------------------------------------------------------------------
Dsrc/uielem.c | 230-------------------------------------------------------------------------------
Asrc/views/bodies.c | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/views/main.c | 293+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/ui/struct.h -> src/views/struct.h | 0
12 files changed, 815 insertions(+), 830 deletions(-)

diff --git a/src/gui.c b/src/gui.c @@ -0,0 +1,313 @@ +#include "main.h" + +static Clickable clickable[CLICKABLE_MAX]; +static int clickablei = 0; + +static void gui_click_tabs(Vector2 mouse, + MouseButton button, Geom *geom, void *elem); +static void gui_click_checkbox(Vector2 mouse, + MouseButton button, Geom *geom, void *elem); +static void gui_click_dropdown(Vector2 mouse, + MouseButton button, Geom *geom, void *elem); +static void gui_click_input(Vector2 mouse, + MouseButton button, Geom *geom, void *elem); + +static void gui_key_input(void *elem, int *fcount); + +static void (*click_handlers[GUI_ELEMS])(Vector2 mouse, + MouseButton button, + Geom *geom, void *elem) = { + [GUI_TAB] = gui_click_tabs, + [GUI_CHECKBOX] = gui_click_checkbox, + [GUI_DROPDOWN] = gui_click_dropdown, + [GUI_INPUT] = gui_click_input, +}; + +void (*gui_key_handlers[GUI_ELEMS])(void *elem, int *fcount) = { + [GUI_TAB] = NULL, + [GUI_CHECKBOX] = NULL, + [GUI_DROPDOWN] = NULL, + [GUI_INPUT] = gui_key_input, +}; + +static void +gui_click_register(Geom geom, enum GuiElements type, void *elem) { + if (clickablei >= CLICKABLE_MAX) + return; /* ran out */ + + clickable[clickablei].geom = geom; + clickable[clickablei].type = type; + clickable[clickablei].elem = elem; + clickablei++; +} + +int +gui_click_handle(void) { + Vector2 mouse; + MouseButton button; + Geom *geom; + int i; + int ret = 0; + int keepfocus = 0; + + mouse = GetMousePosition(); + /* I wish there was a: int GetMouseButton(void) */ + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) + button = MOUSE_BUTTON_LEFT; + else if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE)) + button = MOUSE_BUTTON_MIDDLE; + else if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) + button = MOUSE_BUTTON_RIGHT; + else + button = -1; + + for (i = 0; i < clickablei; i++) { + if (clickable[i].elem && ui_collides(clickable[i].geom, mouse)) { + geom = &clickable[i].geom; + click_handlers[clickable[i].type](mouse, + button, geom, clickable[i].elem); + if (clickable[i].elem == focus.p) + keepfocus = 1; + ret = 1; + } + } + + clickablei = 0; + + /* clicking outside the focused elememnt unfocuses */ + if (button != -1 && !keepfocus) + ui_update_focus(0, NULL); + + return ret; +} + +void +gui_tabs(int x, int y, int w, int h, Tabs *tabs) { + int fw, fn, ftabw; + int tabw; + int padx, pady; + int iw; + int cx, selx = -1; + int i; + + ui_draw_rectangle(x, y, w, h, col_bg); + + for (fw = w, fn = i = 0; i < tabs->n; i++) { + if (!tabs->tabs[i].w) + fn++; + else + fw -= tabs->tabs[i].w; + } + + ftabw = fw / fn; + pady = (h - FONT_SIZE) / 2; + + for (i = 0, cx = x; i < tabs->n; i++, cx += tabw) { + if (i == tabs->n - 1) + tabw = x + w - cx; + else if (!tabs->tabs[i].w) + tabw = ftabw; + else + tabw = tabs->tabs[i].w; + + if (tabs->tabs[i].icon) + iw = tabs->tabs[i].icon->width; + else + iw = 0; + + padx = (tabw - ui_textsize(tabs->tabs[i].name) - iw) / 2; + if (i == tabs->sel) + selx = cx; + else + ui_draw_rectangle(cx, y, tabw, h, col_unselbg); + ui_print(cx + padx + iw, y + pady, col_fg, "%s", tabs->tabs[i].name); + if (tabs->tabs[i].icon) + ui_draw_texture(*tabs->tabs[i].icon, cx + padx / 2, + y + (h - tabs->tabs[i].icon->width) / 2); + ui_draw_rectangle(cx + tabw - 1, y, 1, h, col_border); + } + + if (tabs->sel != tabs->n - 1) { + if (!tabs->tabs[i].w) + tabw = ftabw; + else + tabw = w / tabs->n; + } + + ui_draw_border(x, y, w, h, 1); + if (selx != -1) ui_draw_rectangle(selx - 1, y + h - 1, tabw + 1, 1, col_bg); /* undraw bottom border */ + if (tabs->sel == 0) ui_draw_rectangle(x, y + 1, 1, h - 1, col_bg); /* undraw left border */ + if (tabs->sel == tabs->n - 1) ui_draw_rectangle(x + w - 1, y + 1, 1, h - 1, col_bg); /* undraw right border */ + + gui_click_register(RECT(x, y, w, h), GUI_TAB, tabs); +} + +static void +gui_click_tabs(Vector2 mouse, MouseButton button, Geom *geom, void *elem) { + int ftabw, fw, fn; + int tabw, x, i; + Tabs *tabs = elem; + + if (button != MOUSE_BUTTON_LEFT) + return; + for (fw = geom->w, fn = i = 0; i < tabs->n; i++) { + if (!tabs->tabs[i].w) + fn++; + else + fw -= tabs->tabs[i].w; + } + ftabw = fw / fn; + for (i = 0, x = geom->x; i < tabs->n; x += tabw, i++) { + if (i == tabs->n - 1) + tabw = geom->x + geom->w - x; + else if (!tabs->tabs[i].w) + tabw = ftabw; + else + tabw = tabs->tabs[i].w; + if (mouse.x >= x && mouse.x <= x + tabw) { + tabs->sel = i; + return; + } + } +} + +int +gui_checkbox(int x, int y, Checkbox *box) { + int w, h; + int rw; + + w = h = FONT_SIZE; + ui_draw_border(x, y, w, h, 1); + ui_draw_rectangle(x + 1, y + 1, w - 2, h - 2, + box->enabled ? (box->val ? col_fg : col_bg) : col_border); + ui_print(x + w + (w / 2), y + (h / 6), col_fg, "%s", box->label); + rw = w + (w / 2) + ui_textsize(box->label); + if (box->enabled) + gui_click_register(RECT(x, y, rw, h), + GUI_CHECKBOX, box); + return rw; +} + +static void +gui_click_checkbox(Vector2 mouse, MouseButton button, Geom *geom, void *elem) { + Checkbox *checkbox = elem; + + if (button != MOUSE_BUTTON_LEFT) + return; + checkbox->val = !checkbox->val; +} + +void +gui_dropdown(int x, int y, int w, Dropdown *d) { + int h = FONT_SIZE; + int fh, ph; + int focused; + int i; + + focused = focus.p == d; + fh = h + (focused ? h * d->n : 0); + ph = MIN(fh, (screen.h - x) * 0.75); + + ui_draw_border_around(x, y, w, ph, 1); + + d->rect = RECT(x, y, w, fh + TPY); + d->pane.geom = &d->rect; + pane_begin(&d->pane); + + if (d->sel != -1) + ui_print(x + TPX, y + TPY, col_fg, "%s", d->str[d->sel]); + else if (d->placeholder) + ui_print(x + TPX, y + TPY, col_info, "%s", d->placeholder); + + if (focused) { + ui_draw_rectangle(x, y + h, w, fh - h, col_unselbg); + for (i = 0; i < d->n; i++) { + ui_print(x + TPX, y + TPY + (i+1) * h, col_fg, "%s", d->str[i]); + } + } + + gui_click_register(d->rect, GUI_DROPDOWN, d); + pane_end(); +} + +static void +gui_click_dropdown(Vector2 mouse, MouseButton button, Geom *geom, void *elem) { + Dropdown *drop = elem; + int i; + + if (button != MOUSE_BUTTON_LEFT) + return; + if (focus.p != drop) { + ui_update_focus(GUI_DROPDOWN, drop); + } else { + i = (mouse.y - geom->y) / FONT_SIZE; + if (i != 0 && i <= drop->n) + drop->sel = i - 1; + ui_update_focus(0, NULL); + } +} + +void +gui_input(int x, int y, int w, Input *in) { + int h = FONT_SIZE; + int focused = focus.p == in; + int cw; + + /* Dirty hack: truncate str to length that fits */ + cw = w / charpx - 1; + if (in->len > cw) { + in->len = cw; + in->wstr[cw] = '\0'; + } + if (in->cur > in->len) + in->cur = in->len; + + ui_draw_border_around(x, y, w, h, 1); + ui_draw_rectangle(x, y, w, h, focused ? col_bg : col_unselbg); + if (in->len) + ui_print(x + TPX, y + TPY, col_fg, "%S", in->wstr); + else if (!focused && in->placeholder) + ui_print(x + TPX, y + TPY, col_info, "%s", in->placeholder); + if (focused) { + ui_draw_rectangle(x + TPX + charpx * in->cur, y + TPY, 1, FONT_SIZE, col_fg); + } + gui_click_register(RECT(x, y, w, h), GUI_INPUT, in); +} + +static void +gui_click_input(Vector2 mouse, MouseButton button, Geom *geom, void *elem) { + Input *input = elem; + int i; + + if (button != MOUSE_BUTTON_LEFT) + return; + if (focus.p != input) { + ui_update_focus(GUI_INPUT, input); + } else { + i = (mouse.x - TPX - geom->x + charpx / 2) / charpx; + if (i < input->len) + input->cur = i; + else if (i > 0) + input->cur = input->len; + } +} + +static void +gui_key_input(void *elem, int *fcount) { + wchar_t c = GetCharPressed(); + Input *in = elem; + + if (IsKeyPressed(KEY_ENTER) && in->onenter) { + wcstombs(in->str, in->wstr, INPUT_MAX); + if (in->onenter(in)) + edittrunc(in->wstr, &in->len, &in->cur); + } else if (ui_keyboard_check(KEY_BACKSPACE, fcount) && in->len && in->cur) { + editrm(in->wstr, &in->len, &in->cur); + } else if (ui_keyboard_check(KEY_LEFT, fcount) && in->cur) { + in->cur--; + } else if (ui_keyboard_check(KEY_RIGHT, fcount) && in->cur != in->len) { + in->cur++; + } else if (c && in->len < INPUT_MAX) { + editins(in->wstr, &in->len, &in->cur, INPUT_MAX, c); + } +} diff --git a/src/main.c b/src/main.c @@ -69,7 +69,6 @@ main(void) { BeginDrawing(); ClearBackground(col_bg); - ui_clickable_clear(); view_drawers[view_tabs.sel](); ui_draw_views(); EndDrawing(); diff --git a/src/main.h b/src/main.h @@ -2,7 +2,7 @@ #include <stdarg.h> #include <raylib.h> #include "struct.h" -#include "ui/struct.h" +#include "views/struct.h" #include "style.h" #include "maths.h" #include "../db/db.h" @@ -72,7 +72,7 @@ extern View_sys view_sys; extern int charpx; void ui_init(void); void ui_update_screen(void); -void ui_update_focus(enum UiElements type, void *p); +void ui_update_focus(enum GuiElements type, void *p); int ui_loop(void); void ui_deinit(void); void ui_print(int x, int y, Color col, char *format, ...); @@ -84,9 +84,7 @@ int ui_collides(Geom geom, Vector2 point); int ui_onscreen(Vector2 point); int ui_onscreen_ring(Vector2 centre, float r); int ui_onscreen_circle(Vector2 centre, float r); -void ui_clickable_register(Geom geom, enum UiElements type, void *elem); -int ui_clickable_update(void); -void ui_clickable_clear(void); +int ui_keyboard_check(int key, int *fcount); void ui_keyboard_handle(void); void ui_draw_views(void); void ui_draw_rectangle(int x, int y, int w, int h, Color col); @@ -111,13 +109,13 @@ void ui_draw_view_design(void); void ui_draw_view_sys(void); void ui_draw_view_settings(void); -/* uielem.c */ -extern void (*ui_elem_handlers[UI_ELEMS])(Vector2 mouse, - MouseButton button, Geom *geom, void *elem); -void ui_tabs(int x, int y, int w, int h, Tabs *tabs); -int ui_checkbox(int x, int y, Checkbox *checkbox); /* returns width */ -void ui_dropdown(int x, int y, int w, Dropdown *d); -void ui_input(int x, int y, int w, Input *in); +/* gui.c */ +extern void (*gui_key_handlers[GUI_ELEMS])(void *elem, int *fcount); +int gui_click_handle(void); +void gui_tabs(int x, int y, int w, int h, Tabs *tabs); +int gui_checkbox(int x, int y, Checkbox *checkbox); /* returns width */ +void gui_dropdown(int x, int y, int w, Dropdown *d); +void gui_input(int x, int y, int w, Input *in); /* ui/main.c */ extern View_main view_main; @@ -129,7 +127,6 @@ extern View_bodies view_bodies; void ui_handle_view_bodies(int nowsel); void ui_draw_view_bodies(void); - /* pane.c */ void pane_begin(Pane *f); void pane_end(void); diff --git a/src/struct.h b/src/struct.h @@ -187,17 +187,17 @@ typedef struct { Pane pane; } Dropdown; -enum UiElements { - UI_TAB, - UI_CHECKBOX, - UI_BUTTON, - UI_INPUT, - UI_DROPDOWN, - UI_ELEMS, +enum GuiElements { + GUI_TAB, + GUI_CHECKBOX, + GUI_BUTTON, + GUI_INPUT, + GUI_DROPDOWN, + GUI_ELEMS, }; typedef struct { - enum UiElements type; + enum GuiElements type; void *p; } Focus; @@ -215,7 +215,7 @@ enum UiViews { #define CLICKABLE_MAX 64 typedef struct { Geom geom; - enum UiElements type; + enum GuiElements type; void *elem; } Clickable; diff --git a/src/system.c b/src/system.c @@ -216,13 +216,8 @@ sys_tree_setter(char *dir, char *group, int depth, Tree *t) { case SYSTREE_BODY: b = t->data; - if (body->parent) { - parent = b->parent->name; - } else { - s = t->u->data; - parent = s->name; - } - dbset(save->db.systems, group, "parent", parent); + if (b->parent) + dbset(save->db.systems, group, "parent", b->parent->name); dbsetfloat(dir, group, "radius", b->radius); dbsetfloat(dir, group, "mass", b->mass); diff --git a/src/ui.c b/src/ui.c @@ -6,9 +6,6 @@ #include <wchar.h> #include "main.h" -static Clickable clickable[CLICKABLE_MAX]; - -/* Return 1 for redraw, 0 to keep prev */ void (*view_handlers[UI_VIEW_LAST])(int) = { [UI_VIEW_MAIN] = ui_handle_view_main, [UI_VIEW_COLONIES] = ui_handle_view_colonies, @@ -82,7 +79,7 @@ ui_update_screen(void) { } void -ui_update_focus(enum UiElements type, void *p) { +ui_update_focus(enum GuiElements type, void *p) { focus.type = type; focus.p = p; } @@ -96,7 +93,7 @@ ui_loop(void) { if (IsWindowResized()) ui_update_screen(); ui_keyboard_handle(); - ui_clickable_update(); + gui_click_handle(); return 1; } @@ -188,67 +185,7 @@ ui_onscreen_circle(Vector2 centre, float r) { return CheckCollisionCircleRec(centre, r, RLIFY_RECT(screen.rect)); } -void -ui_clickable_register(Geom geom, enum UiElements type, void *elem) { - int i; - - for (i = 0; i < CLICKABLE_MAX; i++) { - if (!clickable[i].elem) { - clickable[i].geom = geom; - clickable[i].type = type; - clickable[i].elem = elem; - return; - } - } - - /* welp, we ran out */ -} - int -ui_clickable_update(void) { - Vector2 mouse; - MouseButton button; - Geom *geom; - int i; - int ret = 0; - int keepfocus = 0; - - mouse = GetMousePosition(); - /* I wish there was a: int GetMouseButton(void) */ - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) - button = MOUSE_BUTTON_LEFT; - else if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE)) - button = MOUSE_BUTTON_MIDDLE; - else if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) - button = MOUSE_BUTTON_RIGHT; - else - button = -1; - - for (i = 0; i < CLICKABLE_MAX; i++) { - if (clickable[i].elem && ui_collides(clickable[i].geom, mouse)) { - geom = &clickable[i].geom; - ui_elem_handlers[clickable[i].type](mouse, - button, geom, clickable[i].elem); - if (clickable[i].elem == focus.p) - keepfocus = 1; - ret = 1; - } - } - - /* clicking outside the focused elememnt unfocuses */ - if (button != -1 && !keepfocus) - ui_update_focus(0, NULL); - - /* Handle bodies seperately for efficiency: - * - body->pxloc can be used instead of a geometry passed to - * ui_clickable_register() - * - bodies offscreen can't be clicked, so they can be skipped. - */ - - return ret; -} - -static int ui_keyboard_check(int key, int *fcount) { if (IsKeyPressed(key)) { *fcount = -10; @@ -263,8 +200,6 @@ ui_keyboard_check(int key, int *fcount) { void ui_keyboard_handle(void) { static int fcount = 0; - wchar_t c; - Input *in; /* Register multiple backspaces when held. raylib does this with * "characters", but anything other than a "character" has completely @@ -278,39 +213,15 @@ ui_keyboard_handle(void) { if (fcount == (int)TARGET_FPS/15) fcount = 0; - if (focus.p && focus.type == UI_INPUT) { - in = focus.p; - c = GetCharPressed(); - - if (IsKeyPressed(KEY_ENTER) && in->onenter) { - wcstombs(in->str, in->wstr, INPUT_MAX); - if (in->onenter(in)) - edittrunc(in->wstr, &in->len, &in->cur); - } else if (ui_keyboard_check(KEY_BACKSPACE, &fcount) && in->len && in->cur) { - editrm(in->wstr, &in->len, &in->cur); - } else if (ui_keyboard_check(KEY_LEFT, &fcount) && in->cur) { - in->cur--; - } else if (ui_keyboard_check(KEY_RIGHT, &fcount) && in->cur != in->len) { - in->cur++; - } else if (c && in->len < INPUT_MAX) { - editins(in->wstr, &in->len, &in->cur, INPUT_MAX, c); - } - - } -} - -void -ui_clickable_clear(void) { - int i; - for (i = 0; i < CLICKABLE_MAX; i++) - clickable[i].elem = NULL; + if (focus.p && gui_key_handlers[focus.type]) + gui_key_handlers[focus.type](focus.p, &fcount); } void ui_draw_views(void) { int sw = GetScreenWidth(); if (sw > VIEWS_MAX_WIDTH) sw = VIEWS_MAX_WIDTH; - ui_tabs(0, 0, sw, VIEWS_HEIGHT, &view_tabs); + gui_tabs(0, 0, sw, VIEWS_HEIGHT, &view_tabs); } void @@ -416,7 +327,7 @@ ui_vectordist(Vector2 a, Vector2 b) { void ui_draw_tabbed_window(int x, int y, int w, int h, Tabs *tabs) { ui_draw_rectangle(x, y, w, h, col_bg); - ui_tabs(x, y, w, WINDOW_TAB_HEIGHT, tabs); + gui_tabs(x, y, w, WINDOW_TAB_HEIGHT, tabs); ui_draw_border(x, y, w, h, WINDOW_BORDER); } diff --git a/src/ui/bodies.c b/src/ui/bodies.c @@ -1,182 +0,0 @@ -#include "../main.h" - -#define INFOBOXES 3 -#define INFOBOX_H (FONT_SIZE * 11 + PAD * 2) -#define INFOBOX_W ((screen.w - PAD * (1 + INFOBOXES)) / INFOBOXES) -#define BUTTONS 50 -#define W MIN(screen.w / 20, 50) - -static View_bodies *v = &view_bodies; -View_bodies view_bodies = { - .sys = NULL, - .selstar = NULL, - .sel = NULL, - .show = { - .planet = {1, 1, "Show planets"}, - .moon = {1, 1, "Show moons"}, - .dwarf = {1, 1, "Show dwarf planets"}, - .asteroid = {1, 1, "Show asteroids"}, - .comet = {1, 1, "Show comets"}, - .nomineral = {1, 1, "Show bodies without mins"}, - }, - .pane = { - .stars = PANESCROLL, - .bodies = PANESCROLL, - }, -}; - -static int -display_body(Body *body) { - if (body->type == BODY_STAR || !body->parent) - return 0; - if (body->type == BODY_MOON && !v->show.moon.val) - return 0; - if ((body->type == BODY_PLANET || body->parent->type == BODY_PLANET) && - !v->show.planet.val) - return 0; - if ((body->type == BODY_DWARF || body->parent->type == BODY_DWARF) && - !v->show.dwarf.val) - return 0; - if ((body->type == BODY_ASTEROID || body->parent->type == BODY_ASTEROID) && - !v->show.asteroid.val) - return 0; - if ((body->type == BODY_COMET || body->parent->type == BODY_COMET) && - !v->show.comet.val) - return 0; - /* TODO: exclude bodies with no mins */ - return 1; -} - -void -ui_handle_view_bodies(int nowsel) { - Vector2 m = GetMousePosition(); - Tree *t; - Body *body; - int pos, i; - - if (!v->sys) - v->sys = sys_default(); - - if (!nowsel) { - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && - ui_collides(v->bodies, m)) { - pos = (m.y + v->pane.bodies.off - v->bodies.y - PAD) - / FONT_SIZE; - for (t = v->sys->t->d, i = 0; t && i < pos; t = t->n) { - body = t->data; - if (display_body(body)) - if (++i == pos) - v->sel = body; - } - } - } - - if (nowsel) - ui_title("Bodies in %s", v->sys->name); -} - -static int -draw_star(int x, int y, Body *star) { - ui_print(x, y, col_fg, "%s", star->name); - return y + FONT_SIZE; -} - -static int -draw_body(int x, int y, Body *body) { - Color col; - - if (body == v->sel) - col = col_info; - else - col = col_fg; - - ui_print(x, y, col, "%s", body->name); - ui_print(x + 3*W, y, col, "%s", body->parent ? body->parent->name : "-"); - ui_print(x + 5*W, y, col, "%s", bodytype_strify(body)); - return y + FONT_SIZE; -} - -void -ui_draw_view_bodies(void) { - int x, y; - Tree *t; - Body *body; - - v->stars = RECT(PAD, VIEWS_HEIGHT + PAD * 2 + FONT_SIZE, - screen.w - PAD * 2, FONT_SIZE * 6); - v->disp = RECT(v->stars.x, v->stars.y + v->stars.h + PAD, - v->stars.w, FONT_SIZE + PAD); - v->bodies = RECT(v->disp.x, v->disp.y + v->disp.h + PAD/2, - v->disp.w, screen.h - v->bodies.y - INFOBOX_H - BUTTONS); - v->loc = RECT(v->bodies.x, v->bodies.y + v->bodies.h + PAD, - INFOBOX_W, INFOBOX_H); - v->mins = RECT(v->loc.x + v->loc.w + PAD, v->loc.y, - INFOBOX_W, INFOBOX_H); - v->hab = RECT(v->mins.x + v->mins.w + PAD, v->mins.y, - INFOBOX_W, INFOBOX_H); - - if (!v->sel) - v->bodies.h += INFOBOX_H; - - ui_print(PAD, VIEWS_HEIGHT + PAD, col_fg, "[System selection dropdown]"); - - ui_draw_border_around(EXPLODE_RECT(v->stars), 1); - v->pane.stars.geom = &v->stars; - pane_begin(&v->pane.stars); - x = v->stars.x + PAD/2; - y = v->stars.y + PAD/2; - ui_print(x, y, col_fg, "Name"); - y += FONT_SIZE * 1.5; - for (t = v->sys->t->d; t; t = t->n) { - body = t->data; - if (body->type == BODY_STAR) - y = draw_star(x, y, body); - } - pane_end(); - - x = v->disp.x + PAD/2; - y = v->disp.y + PAD/2; - x += ui_checkbox(x, y, &v->show.planet) + PAD * 2; - x += ui_checkbox(x, y, &v->show.moon) + PAD * 2; - x += ui_checkbox(x, y, &v->show.dwarf) + PAD * 2; - x += ui_checkbox(x, y, &v->show.asteroid) + PAD * 2; - x += ui_checkbox(x, y, &v->show.comet) + PAD * 2; - x += ui_checkbox(x, y, &v->show.nomineral); - ui_draw_border_around(v->disp.x, v->disp.y, x, v->disp.h, 1); - - ui_draw_border_around(EXPLODE_RECT(v->bodies), 1); - v->pane.bodies.geom = &v->bodies; - pane_begin(&v->pane.bodies); - x = v->bodies.x + PAD/2; - y = v->bodies.y + PAD/2; - ui_print(x, y, col_fg, "Name"); - ui_print(x + 3*W, y, col_fg, "Parent"); - ui_print(x + 5*W, y, col_fg, "Type"); - y += FONT_SIZE * 1.5; - for (t = v->sys->t->d; t; t = t->n) { - body = t->data; - if (display_body(body)) - y = draw_body(x, y, body); - } - pane_end(); - - if (v->sel) { - ui_draw_border_around(EXPLODE_RECT(v->loc), 1); - x = v->loc.x + PAD/2; - y = v->loc.y + PAD/2; - ui_print(x, y, col_info, "Orbital period:"); - ui_print(x + INFOBOX_W / 2, y, col_fg, - "%.2f days", v->sel->orbdays); - if (v->sel->type != BODY_COMET) { - ui_print(x, y += FONT_SIZE, col_info, "Orbital distance:"); - ui_print(x + INFOBOX_W / 2, y, col_fg, - "%s (%s)", strkm(v->sel->dist), - strly(v->sel->dist)); - } - - ui_draw_border_around(EXPLODE_RECT(v->mins), 1); - ui_draw_border_around(EXPLODE_RECT(v->hab), 1); - } - - DrawFPS(50, 50); -} diff --git a/src/ui/main.c b/src/ui/main.c @@ -1,293 +0,0 @@ -#include <ctype.h> -#include "../main.h" - -static float min_body_rad[] = { - [BODY_STAR] = 4, - [BODY_PLANET] = 3, - [BODY_COMET] = 2, - [BODY_DWARF] = 2, - [BODY_ASTEROID] = 1, - [BODY_MOON] = 1, -}; - -View_main view_main = { - .infobox = { - .tabs = { - 2, 0, {{NULL, "Display", 0}, {NULL, "Minerals", 0}} - }, - .names = { - .dwarf = {1, 1, "Name: dwarf planets"}, - .dwarfn = {1, 0, "Name: numbered dwarf planets"}, /* TODO */ - .asteroid = {1, 0, "Name: asteroids"}, - .asteroidn = {1, 0, "Name: numbered asteroids"}, /* TODO */ - .comet = {1, 1, "Name: comets"}, - }, - .orbit = { - .dwarf = {1, 0, "Orbit: dwarf planets"}, - .asteroid = {1, 0, "Orbit: asteroids"}, - .comet = {1, 0, "Orbit: comets"}, - }, - .comettail = {1, 1, "Comet tails"}, /* TODO */ - .geom = { - .type = UI_RECT, - .x = PAD, - .y = VIEWS_HEIGHT + PAD, - .w = 200, - .h = 400, - }, - .pane = PANESCROLL, - }, - .pan = 0, - .ruler = {.held = 0}, - .kmx = 0, - .kmy = 0, - .kmperpx = 500000, - .scale = { - .x = PAD, - .y = PAD, /* from bottom */ - .w = 50, - .h = 3, - }, - .sys = NULL, -}; - -Vector2 -kmtopx(Vector2 km) { - return (Vector2) { - (GetScreenWidth() / 2) + (km.x - view_main.kmx) / view_main.kmperpx, - (GetScreenHeight() / 2) + (km.y - view_main.kmy) / view_main.kmperpx - }; -} - -Vector2 -pxtokm(Vector2 vector) { - return (Vector2) { - ((vector.x - GetScreenWidth() / 2) * view_main.kmperpx) + view_main.kmx, - ((vector.y - GetScreenHeight() / 2) * view_main.kmperpx) + view_main.kmy - }; -} - -void -ui_handle_view_main(int nowsel) { - Vector2 mouse = GetMousePosition(); - Vector2 delta = GetMouseDelta(); - float wheel = ui_get_scroll(); - float diff; - Body *furth; - - if (view_main.sys) - furth = view_main.sys->furthest_body; - -#define SCROLL_DIVISOR 10 - if (!ui_collides(view_main.infobox.geom, mouse)) { - if (wheel) { - diff = wheel * (view_main.kmperpx/SCROLL_DIVISOR); - if (diff > 0 || !furth || view_main.kmperpx * GetScreenHeight() < - 2 * (furth->type == BODY_COMET ? furth->maxdist : furth->dist)) { - view_main.kmperpx -= diff; - view_main.kmx += (mouse.x - GetScreenWidth() / 2) * diff; - view_main.kmy += (mouse.y - GetScreenHeight() / 2) * diff; - } - } - - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - if (view_main.pan) { - view_main.kmx -= delta.x * view_main.kmperpx; - view_main.kmy -= delta.y * view_main.kmperpx; - } else if (!ui_collides(view_main.infobox.geom, mouse)) { - view_main.pan = 1; - } - } else { - view_main.pan = 0; - } - - if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT) && !view_main.ruler.held) { - view_main.ruler.held = 1; - view_main.ruler.origin = pxtokm(mouse); - } else if (IsMouseButtonUp(MOUSE_BUTTON_RIGHT)) { - view_main.ruler.held = 0; - } - } - - if (!view_main.sys) { - view_main.sys = sys_default(); - } - - if (nowsel) - ui_title("Tactical: %s", view_main.sys->name); - - view_main.infobox.names.dwarfn.enabled = view_main.infobox.names.dwarf.val; - view_main.infobox.names.asteroidn.enabled = view_main.infobox.names.asteroid.val; -} - -static int -should_draw_body_checkbox(Body *body, int type, Checkbox *box) { - if ((body->type == type || (body->parent && - body->parent->type == type)) && - !box->val) - return 0; - return 1; -} - -static void -draw_orbit(Body *body) { - Vector2 parent; - float pxrad; - - if (!body->parent) - return; - if (!should_draw_body_checkbox(body, BODY_DWARF, - &view_main.infobox.orbit.dwarf)) - return; - if (!should_draw_body_checkbox(body, BODY_ASTEROID, - &view_main.infobox.orbit.asteroid)) - return; - if (!should_draw_body_checkbox(body, BODY_COMET, - &view_main.infobox.orbit.comet)) - return; - - parent = kmtopx(body->parent->vector); - pxrad = ui_vectordist(parent, body->pxloc); - - if (pxrad < min_body_rad[body->parent->type]) - return; - - if (body->type == BODY_COMET) - ui_draw_line_v(parent, body->pxloc, 1, col_orbit); - else - ui_draw_ring(parent.x, parent.y, pxrad, col_orbit); -} - -static void -draw_body(Body *body) { - float w; - - if (!ui_onscreen(body->pxloc)) - return; - - /* body */ - if (body->radius / view_main.kmperpx > min_body_rad[body->type]) - w = body->radius / view_main.kmperpx; - else - w = min_body_rad[body->type]; - - if (body->parent && body->type != BODY_COMET && - body->dist / view_main.kmperpx < - min_body_rad[body->parent->type]) - return; - - ui_draw_circle(body->pxloc.x, body->pxloc.y, w, col_body[body->type]); - if (body->type == BODY_COMET && view_main.infobox.comettail.val && - 10 * view_main.kmperpx < body->curdist) - ui_draw_line_v(body->pxloc, sys_vectorize_around(body->pxloc, - (Polar){w * 11 / min_body_rad[BODY_COMET], - body->inward ? body->theta : body->theta + 180}), - w / min_body_rad[BODY_COMET], col_body[BODY_COMET]); - - /* name */ - if (body->type != BODY_STAR && - (body->type == BODY_COMET ? body->maxdist : body->dist) - / view_main.kmperpx < ui_textsize(body->name)) - return; - if (body->parent && body->type != BODY_STAR && - ui_vectordist(body->vector, body->parent->vector) < - min_body_rad[body->type] * view_main.kmperpx) - return; - if (isdigit(*body->name) || *body->name == '(') { - if (!should_draw_body_checkbox(body, BODY_DWARF, - &view_main.infobox.names.dwarfn)) - return; - if (!should_draw_body_checkbox(body, BODY_ASTEROID, - &view_main.infobox.names.asteroidn)) - return; - } - if (!should_draw_body_checkbox(body, BODY_DWARF, - &view_main.infobox.names.dwarf)) - return; - if (!should_draw_body_checkbox(body, BODY_ASTEROID, - &view_main.infobox.names.asteroid)) - return; - if (!should_draw_body_checkbox(body, BODY_COMET, - &view_main.infobox.names.comet)) - return; - - ui_print(body->pxloc.x + w + 2, body->pxloc.y + w + 2, - col_fg, "%s", body->name); -} - -void -ui_draw_view_main(void) { - Vector2 mouse = GetMousePosition(); - Vector2 mousekm = pxtokm(mouse); - Vector2 ruler; - Geom geom; - Tree *t; - Body *body; - float dist; - float x, y; - - /* debug info */ - ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + PAD, col_fg, "W: %f | H: %f", (float)screen.w, (float)screen.h); - ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + PAD * 2, col_fg, "Xoff: %f | Yoff: %f | km/px: %f", - view_main.kmx, view_main.kmy, view_main.kmperpx); - ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + PAD * 3, col_fg, "X: %f | Y: %f", - mousekm.x, mousekm.y); - ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + PAD * 4, col_fg, "FPS: %d (target: %d)", GetFPS(), TARGET_FPS); - - /* draw system bodies */ - for (t = view_main.sys->t->d; t; t = t->n) { - body = t->data; - body->pxloc = kmtopx(body->vector); - draw_orbit(body); - } - for (t = view_main.sys->t->d; t; t = t->n) { - body = t->data; - draw_body(body); - } - - /* ruler */ - if (view_main.ruler.held) { - ruler = kmtopx(view_main.ruler.origin); - ui_draw_line_v(ruler, mouse, 1, col_info); - dist = ui_vectordist(view_main.ruler.origin, mousekm); - ui_print(mouse.x + PAD, mouse.y - PAD, col_info, "%s (%s)", strkm(dist), strly(dist)); - } - - /* scale */ - ui_draw_rectangle(view_main.scale.x, - GetScreenHeight() - view_main.scale.y, - view_main.scale.w, 1, col_info); /* horizontal */ - ui_draw_rectangle(view_main.scale.x, - GetScreenHeight() - view_main.scale.y - view_main.scale.h, - 1, view_main.scale.h, col_info); /* left vertical */ - ui_draw_rectangle(view_main.scale.x + view_main.scale.w, - GetScreenHeight() - view_main.scale.y - view_main.scale.h, - 1, view_main.scale.h, col_info); /* right vertical */ - dist = view_main.scale.w * view_main.kmperpx; - ui_print(view_main.scale.x + view_main.scale.w + FONT_SIZE / 3, - GetScreenHeight() - view_main.scale.y - FONT_SIZE / 2, - col_info, "%s", strkm(dist)); - - /* infobox */ - ui_draw_tabbed_window(EXPLODE_RECT(view_main.infobox.geom), - &view_main.infobox.tabs); - x = view_main.infobox.geom.x + FONT_SIZE; - y = view_main.infobox.geom.y + WINDOW_TAB_HEIGHT; - geom = RECT(x - FONT_SIZE, - y, - view_main.infobox.geom.w - WINDOW_BORDER, - view_main.infobox.geom.h - WINDOW_TAB_HEIGHT - WINDOW_BORDER); - view_main.infobox.pane.geom = &geom; - - pane_begin(&view_main.infobox.pane); - ui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.dwarf); - ui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.dwarfn); - ui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.asteroid); - ui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.asteroidn); - ui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.comet); - ui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.orbit.dwarf); - ui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.orbit.asteroid); - ui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.orbit.comet); - ui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.comettail); - pane_end(); -} diff --git a/src/uielem.c b/src/uielem.c @@ -1,230 +0,0 @@ -#include "main.h" - -static void ui_handle_tabs(Vector2 mouse, - MouseButton button, Geom *geom, void *elem); -static void ui_handle_checkbox(Vector2 mouse, - MouseButton button, Geom *geom, void *elem); -static void ui_handle_dropdown(Vector2 mouse, - MouseButton button, Geom *geom, void *elem); -static void ui_handle_input(Vector2 mouse, - MouseButton button, Geom *geom, void *elem); - -void (*ui_elem_handlers[UI_ELEMS])(Vector2 mouse, - MouseButton button, - Geom *geom, void *elem) = { - [UI_TAB] = ui_handle_tabs, - [UI_CHECKBOX] = ui_handle_checkbox, - [UI_DROPDOWN] = ui_handle_dropdown, - [UI_INPUT] = ui_handle_input, -}; - -void -ui_tabs(int x, int y, int w, int h, Tabs *tabs) { - int fw, fn, ftabw; - int tabw; - int padx, pady; - int iw; - int cx, selx = -1; - int i; - - ui_draw_rectangle(x, y, w, h, col_bg); - - for (fw = w, fn = i = 0; i < tabs->n; i++) { - if (!tabs->tabs[i].w) - fn++; - else - fw -= tabs->tabs[i].w; - } - - ftabw = fw / fn; - pady = (h - FONT_SIZE) / 2; - - for (i = 0, cx = x; i < tabs->n; i++, cx += tabw) { - if (i == tabs->n - 1) - tabw = x + w - cx; - else if (!tabs->tabs[i].w) - tabw = ftabw; - else - tabw = tabs->tabs[i].w; - - if (tabs->tabs[i].icon) - iw = tabs->tabs[i].icon->width; - else - iw = 0; - - padx = (tabw - ui_textsize(tabs->tabs[i].name) - iw) / 2; - if (i == tabs->sel) - selx = cx; - else - ui_draw_rectangle(cx, y, tabw, h, col_unselbg); - ui_print(cx + padx + iw, y + pady, col_fg, "%s", tabs->tabs[i].name); - if (tabs->tabs[i].icon) - ui_draw_texture(*tabs->tabs[i].icon, cx + padx / 2, - y + (h - tabs->tabs[i].icon->width) / 2); - ui_draw_rectangle(cx + tabw - 1, y, 1, h, col_border); - } - - if (tabs->sel != tabs->n - 1) { - if (!tabs->tabs[i].w) - tabw = ftabw; - else - tabw = w / tabs->n; - } - - ui_draw_border(x, y, w, h, 1); - if (selx != -1) ui_draw_rectangle(selx - 1, y + h - 1, tabw + 1, 1, col_bg); /* undraw bottom border */ - if (tabs->sel == 0) ui_draw_rectangle(x, y + 1, 1, h - 1, col_bg); /* undraw left border */ - if (tabs->sel == tabs->n - 1) ui_draw_rectangle(x + w - 1, y + 1, 1, h - 1, col_bg); /* undraw right border */ - - ui_clickable_register(RECT(x, y, w, h), UI_TAB, tabs); -} - -static void -ui_handle_tabs(Vector2 mouse, MouseButton button, Geom *geom, void *elem) { - int ftabw, fw, fn; - int tabw, x, i; - Tabs *tabs = elem; - - if (button != MOUSE_BUTTON_LEFT) - return; - for (fw = geom->w, fn = i = 0; i < tabs->n; i++) { - if (!tabs->tabs[i].w) - fn++; - else - fw -= tabs->tabs[i].w; - } - ftabw = fw / fn; - for (i = 0, x = geom->x; i < tabs->n; x += tabw, i++) { - if (i == tabs->n - 1) - tabw = geom->x + geom->w - x; - else if (!tabs->tabs[i].w) - tabw = ftabw; - else - tabw = tabs->tabs[i].w; - if (mouse.x >= x && mouse.x <= x + tabw) { - tabs->sel = i; - return; - } - } -} - -int -ui_checkbox(int x, int y, Checkbox *box) { - int w, h; - int rw; - - w = h = FONT_SIZE; - ui_draw_border(x, y, w, h, 1); - ui_draw_rectangle(x + 1, y + 1, w - 2, h - 2, - box->enabled ? (box->val ? col_fg : col_bg) : col_border); - ui_print(x + w + (w / 2), y + (h / 6), col_fg, "%s", box->label); - rw = w + (w / 2) + ui_textsize(box->label); - if (box->enabled) - ui_clickable_register(RECT(x, y, rw, h), - UI_CHECKBOX, box); - return rw; -} - -static void -ui_handle_checkbox(Vector2 mouse, MouseButton button, Geom *geom, void *elem) { - Checkbox *checkbox = elem; - - if (button != MOUSE_BUTTON_LEFT) - return; - checkbox->val = !checkbox->val; -} - -void -ui_dropdown(int x, int y, int w, Dropdown *d) { - int h = FONT_SIZE; - int fh, ph; - int focused; - int i; - - focused = focus.p == d; - fh = h + (focused ? h * d->n : 0); - ph = MIN(fh, (screen.h - x) * 0.75); - - ui_draw_border_around(x, y, w, ph, 1); - - d->rect = RECT(x, y, w, fh + TPY); - d->pane.geom = &d->rect; - pane_begin(&d->pane); - - if (d->sel != -1) - ui_print(x + TPX, y + TPY, col_fg, "%s", d->str[d->sel]); - else if (d->placeholder) - ui_print(x + TPX, y + TPY, col_info, "%s", d->placeholder); - - if (focused) { - ui_draw_rectangle(x, y + h, w, fh - h, col_unselbg); - for (i = 0; i < d->n; i++) { - ui_print(x + TPX, y + TPY + (i+1) * h, col_fg, "%s", d->str[i]); - } - } - - ui_clickable_register(d->rect, UI_DROPDOWN, d); - pane_end(); -} - -static void -ui_handle_dropdown(Vector2 mouse, MouseButton button, Geom *geom, void *elem) { - Dropdown *drop = elem; - int i; - - if (button != MOUSE_BUTTON_LEFT) - return; - if (focus.p != drop) { - ui_update_focus(UI_DROPDOWN, drop); - } else { - i = (mouse.y - geom->y) / FONT_SIZE; - if (i != 0 && i <= drop->n) - drop->sel = i - 1; - ui_update_focus(0, NULL); - } -} - -void -ui_input(int x, int y, int w, Input *in) { - int h = FONT_SIZE; - int focused = focus.p == in; - int cw; - - /* Dirty hack: truncate str to length that fits */ - cw = w / charpx - 1; - if (in->len > cw) { - in->len = cw; - in->wstr[cw] = '\0'; - } - if (in->cur > in->len) - in->cur = in->len; - - ui_draw_border_around(x, y, w, h, 1); - ui_draw_rectangle(x, y, w, h, focused ? col_bg : col_unselbg); - if (in->len) - ui_print(x + TPX, y + TPY, col_fg, "%S", in->wstr); - else if (!focused && in->placeholder) - ui_print(x + TPX, y + TPY, col_info, "%s", in->placeholder); - if (focused) { - ui_draw_rectangle(x + TPX + charpx * in->cur, y + TPY, 1, FONT_SIZE, col_fg); - } - ui_clickable_register(RECT(x, y, w, h), UI_INPUT, in); -} - -static void -ui_handle_input(Vector2 mouse, MouseButton button, Geom *geom, void *elem) { - Input *input = elem; - int i; - - if (button != MOUSE_BUTTON_LEFT) - return; - if (focus.p != input) { - ui_update_focus(UI_INPUT, input); - } else { - i = (mouse.x - TPX - geom->x + charpx / 2) / charpx; - if (i < input->len) - input->cur = i; - else if (i > 0) - input->cur = input->len; - } -} diff --git a/src/views/bodies.c b/src/views/bodies.c @@ -0,0 +1,182 @@ +#include "../main.h" + +#define INFOBOXES 3 +#define INFOBOX_H (FONT_SIZE * 11 + PAD * 2) +#define INFOBOX_W ((screen.w - PAD * (1 + INFOBOXES)) / INFOBOXES) +#define BUTTONS 50 +#define W MIN(screen.w / 20, 50) + +static View_bodies *v = &view_bodies; +View_bodies view_bodies = { + .sys = NULL, + .selstar = NULL, + .sel = NULL, + .show = { + .planet = {1, 1, "Show planets"}, + .moon = {1, 1, "Show moons"}, + .dwarf = {1, 1, "Show dwarf planets"}, + .asteroid = {1, 1, "Show asteroids"}, + .comet = {1, 1, "Show comets"}, + .nomineral = {1, 1, "Show bodies without mins"}, + }, + .pane = { + .stars = PANESCROLL, + .bodies = PANESCROLL, + }, +}; + +static int +display_body(Body *body) { + if (body->type == BODY_STAR || !body->parent) + return 0; + if (body->type == BODY_MOON && !v->show.moon.val) + return 0; + if ((body->type == BODY_PLANET || body->parent->type == BODY_PLANET) && + !v->show.planet.val) + return 0; + if ((body->type == BODY_DWARF || body->parent->type == BODY_DWARF) && + !v->show.dwarf.val) + return 0; + if ((body->type == BODY_ASTEROID || body->parent->type == BODY_ASTEROID) && + !v->show.asteroid.val) + return 0; + if ((body->type == BODY_COMET || body->parent->type == BODY_COMET) && + !v->show.comet.val) + return 0; + /* TODO: exclude bodies with no mins */ + return 1; +} + +void +ui_handle_view_bodies(int nowsel) { + Vector2 m = GetMousePosition(); + Tree *t; + Body *body; + int pos, i; + + if (!v->sys) + v->sys = sys_default(); + + if (!nowsel) { + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && + ui_collides(v->bodies, m)) { + pos = (m.y + v->pane.bodies.off - v->bodies.y - PAD) + / FONT_SIZE; + for (t = v->sys->t->d, i = 0; t && i < pos; t = t->n) { + body = t->data; + if (display_body(body)) + if (++i == pos) + v->sel = body; + } + } + } + + if (nowsel) + ui_title("Bodies in %s", v->sys->name); +} + +static int +draw_star(int x, int y, Body *star) { + ui_print(x, y, col_fg, "%s", star->name); + return y + FONT_SIZE; +} + +static int +draw_body(int x, int y, Body *body) { + Color col; + + if (body == v->sel) + col = col_info; + else + col = col_fg; + + ui_print(x, y, col, "%s", body->name); + ui_print(x + 3*W, y, col, "%s", body->parent ? body->parent->name : "-"); + ui_print(x + 5*W, y, col, "%s", bodytype_strify(body)); + return y + FONT_SIZE; +} + +void +ui_draw_view_bodies(void) { + int x, y; + Tree *t; + Body *body; + + v->stars = RECT(PAD, VIEWS_HEIGHT + PAD * 2 + FONT_SIZE, + screen.w - PAD * 2, FONT_SIZE * 6); + v->disp = RECT(v->stars.x, v->stars.y + v->stars.h + PAD, + v->stars.w, FONT_SIZE + PAD); + v->bodies = RECT(v->disp.x, v->disp.y + v->disp.h + PAD/2, + v->disp.w, screen.h - v->bodies.y - INFOBOX_H - BUTTONS); + v->loc = RECT(v->bodies.x, v->bodies.y + v->bodies.h + PAD, + INFOBOX_W, INFOBOX_H); + v->mins = RECT(v->loc.x + v->loc.w + PAD, v->loc.y, + INFOBOX_W, INFOBOX_H); + v->hab = RECT(v->mins.x + v->mins.w + PAD, v->mins.y, + INFOBOX_W, INFOBOX_H); + + if (!v->sel) + v->bodies.h += INFOBOX_H; + + ui_print(PAD, VIEWS_HEIGHT + PAD, col_fg, "[System selection dropdown]"); + + ui_draw_border_around(EXPLODE_RECT(v->stars), 1); + v->pane.stars.geom = &v->stars; + pane_begin(&v->pane.stars); + x = v->stars.x + PAD/2; + y = v->stars.y + PAD/2; + ui_print(x, y, col_fg, "Name"); + y += FONT_SIZE * 1.5; + for (t = v->sys->t->d; t; t = t->n) { + body = t->data; + if (body->type == BODY_STAR) + y = draw_star(x, y, body); + } + pane_end(); + + x = v->disp.x + PAD/2; + y = v->disp.y + PAD/2; + x += gui_checkbox(x, y, &v->show.planet) + PAD * 2; + x += gui_checkbox(x, y, &v->show.moon) + PAD * 2; + x += gui_checkbox(x, y, &v->show.dwarf) + PAD * 2; + x += gui_checkbox(x, y, &v->show.asteroid) + PAD * 2; + x += gui_checkbox(x, y, &v->show.comet) + PAD * 2; + x += gui_checkbox(x, y, &v->show.nomineral); + ui_draw_border_around(v->disp.x, v->disp.y, x, v->disp.h, 1); + + ui_draw_border_around(EXPLODE_RECT(v->bodies), 1); + v->pane.bodies.geom = &v->bodies; + pane_begin(&v->pane.bodies); + x = v->bodies.x + PAD/2; + y = v->bodies.y + PAD/2; + ui_print(x, y, col_fg, "Name"); + ui_print(x + 3*W, y, col_fg, "Parent"); + ui_print(x + 5*W, y, col_fg, "Type"); + y += FONT_SIZE * 1.5; + for (t = v->sys->t->d; t; t = t->n) { + body = t->data; + if (display_body(body)) + y = draw_body(x, y, body); + } + pane_end(); + + if (v->sel) { + ui_draw_border_around(EXPLODE_RECT(v->loc), 1); + x = v->loc.x + PAD/2; + y = v->loc.y + PAD/2; + ui_print(x, y, col_info, "Orbital period:"); + ui_print(x + INFOBOX_W / 2, y, col_fg, + "%.2f days", v->sel->orbdays); + if (v->sel->type != BODY_COMET) { + ui_print(x, y += FONT_SIZE, col_info, "Orbital distance:"); + ui_print(x + INFOBOX_W / 2, y, col_fg, + "%s (%s)", strkm(v->sel->dist), + strly(v->sel->dist)); + } + + ui_draw_border_around(EXPLODE_RECT(v->mins), 1); + ui_draw_border_around(EXPLODE_RECT(v->hab), 1); + } + + DrawFPS(50, 50); +} diff --git a/src/views/main.c b/src/views/main.c @@ -0,0 +1,293 @@ +#include <ctype.h> +#include "../main.h" + +static float min_body_rad[] = { + [BODY_STAR] = 4, + [BODY_PLANET] = 3, + [BODY_COMET] = 2, + [BODY_DWARF] = 2, + [BODY_ASTEROID] = 1, + [BODY_MOON] = 1, +}; + +View_main view_main = { + .infobox = { + .tabs = { + 2, 0, {{NULL, "Display", 0}, {NULL, "Minerals", 0}} + }, + .names = { + .dwarf = {1, 1, "Name: dwarf planets"}, + .dwarfn = {1, 0, "Name: numbered dwarf planets"}, /* TODO */ + .asteroid = {1, 0, "Name: asteroids"}, + .asteroidn = {1, 0, "Name: numbered asteroids"}, /* TODO */ + .comet = {1, 1, "Name: comets"}, + }, + .orbit = { + .dwarf = {1, 0, "Orbit: dwarf planets"}, + .asteroid = {1, 0, "Orbit: asteroids"}, + .comet = {1, 0, "Orbit: comets"}, + }, + .comettail = {1, 1, "Comet tails"}, /* TODO */ + .geom = { + .type = UI_RECT, + .x = PAD, + .y = VIEWS_HEIGHT + PAD, + .w = 200, + .h = 400, + }, + .pane = PANESCROLL, + }, + .pan = 0, + .ruler = {.held = 0}, + .kmx = 0, + .kmy = 0, + .kmperpx = 500000, + .scale = { + .x = PAD, + .y = PAD, /* from bottom */ + .w = 50, + .h = 3, + }, + .sys = NULL, +}; + +Vector2 +kmtopx(Vector2 km) { + return (Vector2) { + (GetScreenWidth() / 2) + (km.x - view_main.kmx) / view_main.kmperpx, + (GetScreenHeight() / 2) + (km.y - view_main.kmy) / view_main.kmperpx + }; +} + +Vector2 +pxtokm(Vector2 vector) { + return (Vector2) { + ((vector.x - GetScreenWidth() / 2) * view_main.kmperpx) + view_main.kmx, + ((vector.y - GetScreenHeight() / 2) * view_main.kmperpx) + view_main.kmy + }; +} + +void +ui_handle_view_main(int nowsel) { + Vector2 mouse = GetMousePosition(); + Vector2 delta = GetMouseDelta(); + float wheel = ui_get_scroll(); + float diff; + Body *furth; + + if (view_main.sys) + furth = view_main.sys->furthest_body; + +#define SCROLL_DIVISOR 10 + if (!ui_collides(view_main.infobox.geom, mouse)) { + if (wheel) { + diff = wheel * (view_main.kmperpx/SCROLL_DIVISOR); + if (diff > 0 || !furth || view_main.kmperpx * GetScreenHeight() < + 2 * (furth->type == BODY_COMET ? furth->maxdist : furth->dist)) { + view_main.kmperpx -= diff; + view_main.kmx += (mouse.x - GetScreenWidth() / 2) * diff; + view_main.kmy += (mouse.y - GetScreenHeight() / 2) * diff; + } + } + + if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { + if (view_main.pan) { + view_main.kmx -= delta.x * view_main.kmperpx; + view_main.kmy -= delta.y * view_main.kmperpx; + } else if (!ui_collides(view_main.infobox.geom, mouse)) { + view_main.pan = 1; + } + } else { + view_main.pan = 0; + } + + if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT) && !view_main.ruler.held) { + view_main.ruler.held = 1; + view_main.ruler.origin = pxtokm(mouse); + } else if (IsMouseButtonUp(MOUSE_BUTTON_RIGHT)) { + view_main.ruler.held = 0; + } + } + + if (!view_main.sys) { + view_main.sys = sys_default(); + } + + if (nowsel) + ui_title("Tactical: %s", view_main.sys->name); + + view_main.infobox.names.dwarfn.enabled = view_main.infobox.names.dwarf.val; + view_main.infobox.names.asteroidn.enabled = view_main.infobox.names.asteroid.val; +} + +static int +should_draw_body_checkbox(Body *body, int type, Checkbox *box) { + if ((body->type == type || (body->parent && + body->parent->type == type)) && + !box->val) + return 0; + return 1; +} + +static void +draw_orbit(Body *body) { + Vector2 parent; + float pxrad; + + if (!body->parent) + return; + if (!should_draw_body_checkbox(body, BODY_DWARF, + &view_main.infobox.orbit.dwarf)) + return; + if (!should_draw_body_checkbox(body, BODY_ASTEROID, + &view_main.infobox.orbit.asteroid)) + return; + if (!should_draw_body_checkbox(body, BODY_COMET, + &view_main.infobox.orbit.comet)) + return; + + parent = kmtopx(body->parent->vector); + pxrad = ui_vectordist(parent, body->pxloc); + + if (pxrad < min_body_rad[body->parent->type]) + return; + + if (body->type == BODY_COMET) + ui_draw_line_v(parent, body->pxloc, 1, col_orbit); + else + ui_draw_ring(parent.x, parent.y, pxrad, col_orbit); +} + +static void +draw_body(Body *body) { + float w; + + if (!ui_onscreen(body->pxloc)) + return; + + /* body */ + if (body->radius / view_main.kmperpx > min_body_rad[body->type]) + w = body->radius / view_main.kmperpx; + else + w = min_body_rad[body->type]; + + if (body->parent && body->type != BODY_COMET && + body->dist / view_main.kmperpx < + min_body_rad[body->parent->type]) + return; + + ui_draw_circle(body->pxloc.x, body->pxloc.y, w, col_body[body->type]); + if (body->type == BODY_COMET && view_main.infobox.comettail.val && + 10 * view_main.kmperpx < body->curdist) + ui_draw_line_v(body->pxloc, sys_vectorize_around(body->pxloc, + (Polar){w * 11 / min_body_rad[BODY_COMET], + body->inward ? body->theta : body->theta + 180}), + w / min_body_rad[BODY_COMET], col_body[BODY_COMET]); + + /* name */ + if (body->type != BODY_STAR && + (body->type == BODY_COMET ? body->maxdist : body->dist) + / view_main.kmperpx < ui_textsize(body->name)) + return; + if (body->parent && body->type != BODY_STAR && + ui_vectordist(body->vector, body->parent->vector) < + min_body_rad[body->type] * view_main.kmperpx) + return; + if (isdigit(*body->name) || *body->name == '(') { + if (!should_draw_body_checkbox(body, BODY_DWARF, + &view_main.infobox.names.dwarfn)) + return; + if (!should_draw_body_checkbox(body, BODY_ASTEROID, + &view_main.infobox.names.asteroidn)) + return; + } + if (!should_draw_body_checkbox(body, BODY_DWARF, + &view_main.infobox.names.dwarf)) + return; + if (!should_draw_body_checkbox(body, BODY_ASTEROID, + &view_main.infobox.names.asteroid)) + return; + if (!should_draw_body_checkbox(body, BODY_COMET, + &view_main.infobox.names.comet)) + return; + + ui_print(body->pxloc.x + w + 2, body->pxloc.y + w + 2, + col_fg, "%s", body->name); +} + +void +ui_draw_view_main(void) { + Vector2 mouse = GetMousePosition(); + Vector2 mousekm = pxtokm(mouse); + Vector2 ruler; + Geom geom; + Tree *t; + Body *body; + float dist; + float x, y; + + /* debug info */ + ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + PAD, col_fg, "W: %f | H: %f", (float)screen.w, (float)screen.h); + ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + PAD * 2, col_fg, "Xoff: %f | Yoff: %f | km/px: %f", + view_main.kmx, view_main.kmy, view_main.kmperpx); + ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + PAD * 3, col_fg, "X: %f | Y: %f", + mousekm.x, mousekm.y); + ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + PAD * 4, col_fg, "FPS: %d (target: %d)", GetFPS(), TARGET_FPS); + + /* draw system bodies */ + for (t = view_main.sys->t->d; t; t = t->n) { + body = t->data; + body->pxloc = kmtopx(body->vector); + draw_orbit(body); + } + for (t = view_main.sys->t->d; t; t = t->n) { + body = t->data; + draw_body(body); + } + + /* ruler */ + if (view_main.ruler.held) { + ruler = kmtopx(view_main.ruler.origin); + ui_draw_line_v(ruler, mouse, 1, col_info); + dist = ui_vectordist(view_main.ruler.origin, mousekm); + ui_print(mouse.x + PAD, mouse.y - PAD, col_info, "%s (%s)", strkm(dist), strly(dist)); + } + + /* scale */ + ui_draw_rectangle(view_main.scale.x, + GetScreenHeight() - view_main.scale.y, + view_main.scale.w, 1, col_info); /* horizontal */ + ui_draw_rectangle(view_main.scale.x, + GetScreenHeight() - view_main.scale.y - view_main.scale.h, + 1, view_main.scale.h, col_info); /* left vertical */ + ui_draw_rectangle(view_main.scale.x + view_main.scale.w, + GetScreenHeight() - view_main.scale.y - view_main.scale.h, + 1, view_main.scale.h, col_info); /* right vertical */ + dist = view_main.scale.w * view_main.kmperpx; + ui_print(view_main.scale.x + view_main.scale.w + FONT_SIZE / 3, + GetScreenHeight() - view_main.scale.y - FONT_SIZE / 2, + col_info, "%s", strkm(dist)); + + /* infobox */ + ui_draw_tabbed_window(EXPLODE_RECT(view_main.infobox.geom), + &view_main.infobox.tabs); + x = view_main.infobox.geom.x + FONT_SIZE; + y = view_main.infobox.geom.y + WINDOW_TAB_HEIGHT; + geom = RECT(x - FONT_SIZE, + y, + view_main.infobox.geom.w - WINDOW_BORDER, + view_main.infobox.geom.h - WINDOW_TAB_HEIGHT - WINDOW_BORDER); + view_main.infobox.pane.geom = &geom; + + pane_begin(&view_main.infobox.pane); + gui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.dwarf); + gui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.dwarfn); + gui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.asteroid); + gui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.asteroidn); + gui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.names.comet); + gui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.orbit.dwarf); + gui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.orbit.asteroid); + gui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.orbit.comet); + gui_checkbox(x, y += FONT_SIZE*1.5, &view_main.infobox.comettail); + pane_end(); +} diff --git a/src/ui/struct.h b/src/views/struct.h