commit e49272570ff63ab0afbd4a74f3a97ab4f150fb07
parent c4b9af2f720b7261863f2d671b4ee24babcbec3c
Author: hhvn <dev@hhvn.uk>
Date: Sat, 8 Oct 2022 16:17:45 +0100
Reorganize gui and views
Diffstat:
A | src/gui.c | | | 313 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/main.c | | | 1 | - |
M | src/main.h | | | 23 | ++++++++++------------- |
M | src/struct.h | | | 18 | +++++++++--------- |
M | src/system.c | | | 9 | ++------- |
M | src/ui.c | | | 101 | +++++-------------------------------------------------------------------------- |
D | src/ui/bodies.c | | | 182 | ------------------------------------------------------------------------------- |
D | src/ui/main.c | | | 293 | ------------------------------------------------------------------------------- |
D | src/uielem.c | | | 230 | ------------------------------------------------------------------------------- |
A | src/views/bodies.c | | | 182 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/views/main.c | | | 293 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
R | src/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