aboutsummaryrefslogtreecommitdiffstats
path: root/src/ui
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2023-11-16 17:44:47 -0600
committerLibravatar bigfoot547 <[email protected]>2023-11-16 17:44:47 -0600
commitff292c3477497e03d4cffc1467691e1e6ce54a5c (patch)
treeb7cd01d3f440dafd07bc7538c116997f7fd3ac01 /src/ui
initial commit
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/base.c226
-rw-r--r--src/ui/dock.c183
-rw-r--r--src/ui/ui.internal.h81
3 files changed, 490 insertions, 0 deletions
diff --git a/src/ui/base.c b/src/ui/base.c
new file mode 100644
index 0000000..74855b1
--- /dev/null
+++ b/src/ui/base.c
@@ -0,0 +1,226 @@
+#include <curses.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "ui.internal.h"
+
+struct ui_window_root *ui_root = NULL;
+
+void ui__default_draw_proc(struct ui_window_base *base)
+{
+ redrawwin(base->cwindow);
+ wrefresh(base->cwindow);
+}
+
+void ui__init_window_base(struct ui_window_base *base)
+{
+ base->type = UI__WINDOW_TYPE_BASE;
+ base->parent = NULL;
+ base->cwindow = NULL;
+ base->draw_proc = &ui__default_draw_proc;
+ base->layout_proc = NULL;
+}
+
+void ui__init_window_dock(struct ui_window_dock *dock)
+{
+ ui__init_window_base(&dock->super);
+ dock->super.type = UI__WINDOW_TYPE_DOCK;
+ dock->super.draw_proc = &ui__dock_default_draw_proc;
+ dock->super.layout_proc = &ui__dock_default_layout_proc;
+
+ for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i)
+ {
+ dock->children[i] = NULL;
+ dock->childsizes[i] = 0.0;
+ }
+
+ dock->focus = UI__WINDOW_FOCUS_NONE;
+}
+
+void ui__init_window_root(struct ui_window_root *root)
+{
+ ui__init_window_dock(&root->super);
+ root->super.super.type = UI__WINDOW_TYPE_ROOT;
+ root->super.super.draw_proc = &ui__root_draw_proc;
+ root->super.super.layout_proc = &ui__root_layout_proc;
+
+ root->floating = NULL;
+}
+
+/* type-specific destructors */
+void ui__destroy_window_base(struct ui_window_base *base);
+void ui__destroy_window_dock(struct ui_window_dock *dock);
+
+void ui__window_destroy_root(struct ui_window_root *root)
+{
+ if (root->floating)
+ ui__destroy_window(root->floating);
+
+ ui__destroy_window_dock(&root->super);
+}
+
+void ui__destroy_window_dock(struct ui_window_dock *dock)
+{
+ for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i)
+ {
+ if (dock->children[i]) ui__destroy_window(dock->children[i]);
+ dock->children[i] = NULL;
+ }
+
+ ui__destroy_window_base(&dock->super);
+}
+
+void ui__destroy_window_base(struct ui_window_base *base)
+{
+ delwin(base->cwindow);
+ free(base);
+}
+
+void ui__destroy_window(struct ui_window_base *base)
+{
+ if (!base) return;
+
+ switch (base->type)
+ {
+ case UI__WINDOW_TYPE_ROOT:
+ ui__window_destroy_root((struct ui_window_root *)base);
+ break;
+ case UI__WINDOW_TYPE_DOCK:
+ ui__destroy_window_dock((struct ui_window_dock *)base);
+ break;
+ case UI__WINDOW_TYPE_BASE:
+ ui__destroy_window_base(base);
+ }
+}
+
+/* the focused window MUST be a leaf window, or the screen will get clobbered.
+ * This is because the focused window will have methods like getch() called on it,
+ * which brings it to the "foreground", squashing whatever is behind it. */
+struct ui_window_base *ui__find_focused(void)
+{
+ struct ui_window_base *window = (struct ui_window_base *)ui_root;
+ if (ui_root->floating) return ui_root->floating;
+
+ while (true)
+ {
+ switch (window->type)
+ {
+ case UI__WINDOW_TYPE_DOCK:
+ case UI__WINDOW_TYPE_ROOT:
+ {
+ struct ui_window_dock *dock = (struct ui_window_dock *)window;
+
+ if (dock->focus == UI__WINDOW_FOCUS_NONE || !dock->children[dock->focus])
+ {
+ for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i)
+ {
+ if (dock->children[i])
+ {
+ dock->focus = i; /* focus the child (skip fallback return statement) */
+ goto childfound;
+ }
+ }
+
+ return (struct ui_window_base *)dock; /* the dock is focused if it is really a leaf */
+ }
+
+childfound:
+ window = dock->children[dock->focus];
+ break;
+ }
+ default:
+ return window; /* a leaf window is focused */
+ }
+ }
+}
+
+void ui__call_draw_proc(struct ui_window_base *base)
+{
+ if (base->draw_proc)
+ {
+ (*base->draw_proc)(base);
+ }
+}
+
+void ui__call_layout_proc(struct ui_window_base *base)
+{
+ if (base->layout_proc)
+ {
+ (*base->layout_proc)(base);
+ }
+}
+
+void ui__traces_draw_proc(struct ui_window_base *base)
+{
+ box(base->cwindow, 0, 0);
+ int maxy, maxx;
+
+ mvwaddstr(base->cwindow, 0, 2, "Traces");
+ getmaxyx(base->cwindow, maxy, maxx);
+ for (int i = 1; i < maxy-1; ++i)
+ {
+ mvwhline(base->cwindow, i, 1, '%', maxx-2);
+ }
+ touchwin(base->cwindow);
+ wrefresh(base->cwindow);
+}
+
+void ui_init(void)
+{
+ ui_root = malloc(sizeof(struct ui_window_root)); /* TODO: check */
+ assert(ui_root);
+
+ initscr();
+
+ raw();
+ noecho();
+ keypad(stdscr, TRUE);
+
+ curs_set(0);
+
+ ui__init_window_root(ui_root);
+ ui_root->super.super.cwindow = stdscr; /* initialize the ncurses window ourselves */
+
+ /* set up UI */
+
+ for (int i = 0; i < UI__WINDOW_DOCK_MAX; ++i)
+ {
+ if (i == UI__WINDOW_DOCK_LEFT) continue;
+ struct ui_window_base *win_traces = malloc(sizeof(struct ui_window_base));
+ ui__init_window_base(win_traces);
+ ui__dock_add_child((struct ui_window_dock *)ui_root, win_traces, i, 1./5);
+ win_traces->draw_proc = &ui__traces_draw_proc;
+ }
+
+ ui__call_draw_proc((struct ui_window_base *)ui_root);
+}
+
+void ui_handle(void)
+{
+ while (true)
+ {
+ struct ui_window_base *window = ui__find_focused();
+
+#ifdef NCURSES_WIDE
+ wint_t inp;
+ wget_wch(window->cwindow, &inp);
+#else
+ int inp = wgetch(window->cwindow);
+#endif
+
+ if (inp == NS('q'))
+ {
+ break;
+ }
+ else if (inp == KEY_RESIZE)
+ {
+ ui__call_layout_proc((struct ui_window_base *)ui_root);
+ ui__call_draw_proc((struct ui_window_base *)ui_root);
+ }
+ }
+
+ ui__window_destroy_root(ui_root);
+ ui_root = NULL;
+
+ endwin();
+}
diff --git a/src/ui/dock.c b/src/ui/dock.c
new file mode 100644
index 0000000..5b59b71
--- /dev/null
+++ b/src/ui/dock.c
@@ -0,0 +1,183 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include "ui.internal.h"
+
+unsigned ui__dock_position_opposite(unsigned position)
+{
+ switch (position)
+ {
+ case UI__WINDOW_DOCK_TOP:
+ return UI__WINDOW_DOCK_BOTTOM;
+ case UI__WINDOW_DOCK_BOTTOM:
+ return UI__WINDOW_DOCK_TOP;
+ case UI__WINDOW_DOCK_LEFT:
+ return UI__WINDOW_DOCK_RIGHT;
+ case UI__WINDOW_DOCK_RIGHT:
+ return UI__WINDOW_DOCK_LEFT;
+ default:
+ assert(false); /* trap: this function should never be called here! */
+ }
+}
+
+void ui__dock_adjust(struct ui_window_dock *dock, unsigned flags, int *maxy, int *maxx, int *begy, int *begx)
+{
+ if (flags & 1)
+ {
+ if (dock->children[UI__WINDOW_DOCK_TOP])
+ {
+ unsigned top_maxy = getmaxy(dock->children[UI__WINDOW_DOCK_TOP]->cwindow);
+ *maxy -= top_maxy;
+ *begy += top_maxy;
+ }
+
+ if (dock->children[UI__WINDOW_DOCK_BOTTOM])
+ {
+ unsigned bottom_maxy = getmaxy(dock->children[UI__WINDOW_DOCK_BOTTOM]->cwindow);
+ *maxy -= bottom_maxy;
+ }
+ }
+
+ if (flags & 2)
+ {
+ if (dock->children[UI__WINDOW_DOCK_LEFT])
+ {
+ unsigned left_maxx = getmaxx(dock->children[UI__WINDOW_DOCK_LEFT]->cwindow);
+ *maxx -= left_maxx;
+ *begx += left_maxx;
+ }
+
+ if (dock->children[UI__WINDOW_DOCK_RIGHT])
+ {
+ unsigned right_maxx = getmaxx(dock->children[UI__WINDOW_DOCK_RIGHT]->cwindow);
+ *maxx -= right_maxx;
+ }
+ }
+}
+
+/* This is hand-coded to fit with the window priorities, as specified here in ascending order:
+ * - Top/bottom
+ * - Left/right
+ * - Center
+ *
+ * Note that other methods (layout proc namely) require this same order to be present in the values of the dock constants. */
+WINDOW *ui__dock_place_window(struct ui_window_dock *dock, unsigned position, float size)
+{
+ int maxy, maxx;
+ int begy, begx;
+
+ int out_lines, out_cols;
+ int out_y, out_x;
+
+ getmaxyx(dock->super.cwindow, maxy, maxx);
+ getbegyx(dock->super.cwindow, begy, begx);
+
+ switch (position)
+ {
+ case UI__WINDOW_DOCK_TOP:
+ out_lines = maxy * size;
+ out_y = begy;
+ out_cols = maxx;
+ out_x = begx;
+ break;
+ case UI__WINDOW_DOCK_BOTTOM:
+ out_lines = maxy * size;
+ out_y = maxy - out_lines + begy;
+ out_cols = maxx;
+ out_x = begx;
+ break;
+ case UI__WINDOW_DOCK_LEFT:
+ ui__dock_adjust(dock, 1, &maxy, &maxx, &begy, &begx);
+ out_lines = maxy;
+ out_y = begy;
+ out_cols = maxx * size;
+ out_x = begx;
+ break;
+ case UI__WINDOW_DOCK_RIGHT:
+ ui__dock_adjust(dock, 1, &maxy, &maxx, &begy, &begx);
+ out_lines = maxy;
+ out_y = begy;
+ out_cols = maxx * size;
+ out_x = maxx - out_cols + begx;
+ break;
+ case UI__WINDOW_DOCK_CENTER:
+ ui__dock_adjust(dock, 3, &maxy, &maxx, &begy, &begx);
+ out_lines = maxy;
+ out_y = begy;
+ out_cols = maxx;
+ out_x = begx;
+ break;
+ default:
+ assert(false);
+ }
+
+ return newwin(out_lines, out_cols, out_y, out_x);
+}
+
+void ui__dock_add_child(struct ui_window_dock *dock, struct ui_window_base *child, unsigned position, float size)
+{
+ assert(!dock->children[position]); /* TODO: handle gracefully (technically invalid usage) */
+ assert(!child->parent); /* TODO: take this window from its current parent */
+ assert(!child->cwindow);
+
+ child->parent = (struct ui_window_base *)dock;
+ dock->children[position] = child;
+ dock->childsizes[position] = size;
+
+ if (position != UI__WINDOW_DOCK_CENTER)
+ {
+ unsigned opposite = ui__dock_position_opposite(position);
+ if (dock->children[opposite] && size + dock->childsizes[opposite] > 1)
+ {
+ dock->childsizes[position] = 1 - dock->childsizes[opposite];
+ }
+ }
+
+ /* now set up the window for this child */
+ child->cwindow = ui__dock_place_window(dock, position, size);
+}
+
+void ui__dock_default_draw_proc(struct ui_window_base *base)
+{
+ struct ui_window_dock *dock = (struct ui_window_dock *)base;
+
+ wrefresh(dock->super.cwindow);
+ for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i)
+ {
+ if (dock->children[i])
+ ui__call_draw_proc(dock->children[i]);
+ }
+}
+
+void ui__dock_default_layout_proc(struct ui_window_base *base)
+{
+ struct ui_window_dock *dock = (struct ui_window_dock *)base;
+
+ /* fix the layout of children */
+ for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i)
+ {
+ struct ui_window_base *child = dock->children[i];
+ if (!child) continue;
+
+ delwin(child->cwindow);
+ child->cwindow = ui__dock_place_window(dock, i, dock->childsizes[i]);
+ ui__call_layout_proc(child);
+ }
+}
+
+void ui__root_draw_proc(struct ui_window_base *base)
+{
+ struct ui_window_root *root = (struct ui_window_root *)base;
+ ui__dock_default_draw_proc(base);
+
+ if (root->floating) ui__call_draw_proc(root->floating);
+}
+
+void ui__root_layout_proc(struct ui_window_base *base)
+{
+ struct ui_window_root *root = (struct ui_window_root *)base;
+ ui__dock_default_layout_proc(base);
+
+ /* TODO: adjust floating window position :) */
+ if (root->floating) ui__call_layout_proc(root->floating);
+}
diff --git a/src/ui/ui.internal.h b/src/ui/ui.internal.h
new file mode 100644
index 0000000..7c53bd4
--- /dev/null
+++ b/src/ui/ui.internal.h
@@ -0,0 +1,81 @@
+#ifndef UMPS_UI_INTERNAL_H_INCLUDED
+#define UMPS_UI_INTERNAL_H_INCLUDED
+
+#include "../ui.h"
+#include "config.h"
+#include NCURSES_INCLUDE
+
+#define UI__WINDOW_DOCK_TOP (0u)
+#define UI__WINDOW_DOCK_BOTTOM (1u)
+#define UI__WINDOW_DOCK_LEFT (2u)
+#define UI__WINDOW_DOCK_RIGHT (3u)
+#define UI__WINDOW_DOCK_CENTER (4u)
+
+#define UI__WINDOW_FOCUS_NONE (999u)
+
+#define UI__WINDOW_TYPE_BASE (0u)
+#define UI__WINDOW_TYPE_DOCK (1u)
+#define UI__WINDOW_TYPE_ROOT (2u)
+
+#define UI__WINDOW_DOCK_MAX UI__WINDOW_DOCK_CENTER+1
+
+/* concrete type definitions */
+
+/* called to refresh the window (should refresh children as well) */
+typedef void (ui__draw_proc)(struct ui_window_base *);
+
+/* called to recalculate the layout of the window (for resize) */
+typedef void (ui__layout_proc)(struct ui_window_base *);
+
+struct ui_window_base {
+ unsigned type;
+ struct ui_window_base *parent; /* the parent of a window manages its memory */
+ WINDOW *cwindow; /* ncurses window */
+
+ ui__draw_proc *draw_proc;
+ ui__layout_proc *layout_proc;
+};
+
+struct ui_window_dock {
+ struct ui_window_base super;
+
+ struct ui_window_base *children[UI__WINDOW_DOCK_MAX];
+ float childsizes[UI__WINDOW_DOCK_MAX];
+
+ unsigned focus;
+};
+
+struct ui_window_root {
+ struct ui_window_dock super;
+
+ struct ui_window_base *floating;
+};
+
+/* internal utils */
+
+/* in-place constructors */
+void ui__init_window_base(struct ui_window_base *);
+void ui__init_window_dock(struct ui_window_dock *);
+void ui__init_window_root(struct ui_window_root *);
+
+struct ui_window_base *ui__find_focused(void);
+
+/* callback utils */
+void ui__call_draw_proc(struct ui_window_base *);
+void ui__call_layout_proc(struct ui_window_base *);
+
+/* destructor */
+
+void ui__destroy_window(struct ui_window_base *);
+
+/* docked window utils */
+
+void ui__dock_add_child(struct ui_window_dock *, struct ui_window_base *, unsigned position, float size);
+void ui__dock_default_draw_proc(struct ui_window_base *base);
+void ui__dock_default_layout_proc(struct ui_window_base *base);
+
+/* root window hooks */
+void ui__root_draw_proc(struct ui_window_base *);
+void ui__root_layout_proc(struct ui_window_base *);
+
+#endif /* include guard */