diff options
| -rw-r--r-- | meson.build | 2 | ||||
| -rw-r--r-- | src/meson.build | 2 | ||||
| -rw-r--r-- | src/ui/base.c | 6 | ||||
| -rw-r--r-- | src/ui/dock.c | 8 | ||||
| -rw-r--r-- | src/ui/menu.c | 118 | ||||
| -rw-r--r-- | src/ui/root.c | 45 | ||||
| -rw-r--r-- | src/ui/ui.internal.h | 3 | ||||
| -rw-r--r-- | src/ui/uimenu.internal.h | 51 |
8 files changed, 222 insertions, 13 deletions
diff --git a/meson.build b/meson.build index 5fb8483..134a16f 100644 --- a/meson.build +++ b/meson.build @@ -27,4 +27,4 @@ endif subdir('src') executable('umps', umps_srcs, dependencies : [curses_dep], include_directories : umps_config_inc, - override_options : {'b_ndebug' : 'if-release'}) + override_options : {'b_ndebug' : 'if-release', 'c_std' : 'c99' }) diff --git a/src/meson.build b/src/meson.build index 14e6269..03038df 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,4 +1,4 @@ -umps_srcs = files('main.c', 'ui/base.c', 'ui/dock.c', 'ui/root.c', 'ui/debug.c') +umps_srcs = files('main.c', 'ui/base.c', 'ui/dock.c', 'ui/root.c', 'ui/debug.c', 'ui/menu.c') configure_file(input : 'config.h.in', output : 'config.h', configuration : conf_data) umps_config_inc = include_directories('.') diff --git a/src/ui/base.c b/src/ui/base.c index f251a3a..d914acf 100644 --- a/src/ui/base.c +++ b/src/ui/base.c @@ -3,6 +3,7 @@ #include <string.h> #include "ui.internal.h" +#include "ui/uimenu.internal.h" struct ui_window_root *ui_root = NULL; @@ -47,6 +48,9 @@ void ui__init_window_root(struct ui_window_root *root, WINDOW *cwindow) root->undersize_scr = false; root->content = NULL; root->floating = NULL; + + root->menu_root = malloc(sizeof(struct uimenu_item_menu)); + uimenu_item_menu_init(root->menu_root, NULL); } /* type-specific destructors */ @@ -61,6 +65,8 @@ void ui__window_destroy_root(struct ui_window_root *root) if (root->floating) ui__destroy_window(root->floating); + uimenu_menu_free(root->menu_root); + ui__destroy_window_base(&root->super); } diff --git a/src/ui/dock.c b/src/ui/dock.c index 178127f..fd895c2 100644 --- a/src/ui/dock.c +++ b/src/ui/dock.c @@ -141,8 +141,14 @@ void ui__dock_add_child(struct ui_window_dock *dock, struct ui_window_base *chil void ui__dock_default_draw_proc(struct ui_window_base *base) { struct ui_window_dock *dock = ui__cast(dock, base); + int maxy, maxx; + getmaxyx(base->cwindow, maxy, maxx); + + for (int i = 0; i < maxy; ++i) { + mvwhline(base->cwindow, i, 0, '.', maxx); + } - wrefresh(dock->super.cwindow); + wnoutrefresh(dock->super.cwindow); for (unsigned i = 0; i < UI__WINDOW_DOCK_MAX; ++i) { if (dock->children[i]) diff --git a/src/ui/menu.c b/src/ui/menu.c new file mode 100644 index 0000000..c0e8d58 --- /dev/null +++ b/src/ui/menu.c @@ -0,0 +1,118 @@ +#include <string.h> +#include <stdlib.h> + +#include "uimenu.internal.h" +#include "../macros.h" + +void uimenu_item_button_init(struct uimenu_item_button *button, unsigned id, const char *text, uimenu_button_action *action) +{ + button->header.type = UMPS__MENU_TYPE_BUTTON; + button->header.next = button->header.prev = NULL; + + button->id = id; + button->text = text ? strdup(text) : NULL; + button->enabled = true; + button->action = action; +} + +void uimenu_item_menu_init(struct uimenu_item_menu *menu, const char *text) +{ + menu->header.type = UMPS__MENU_TYPE_MENU; + menu->header.next = menu->header.prev = NULL; + + menu->text = text ? strdup(text) : NULL; + menu->head = menu->tail = NULL; + menu->nchildren = 0; +} + +struct uimenu_item_header *uimenu__item_menu_spacer_create(void) +{ + struct uimenu_item_header *spacer = malloc(sizeof(struct uimenu_item_header)); + spacer->type = UMPS__MENU_TYPE_SPACER; + spacer->next = spacer->prev = NULL; + return spacer; +} + +/* menu items will be freed on removal */ +void uimenu_menu_add_spacer(struct uimenu_item_menu *menu, struct uimenu_item_header *where, bool after) +{ + struct uimenu_item_header *spc = uimenu__item_menu_spacer_create(); + uimenu_menu_add(menu, where, spc, after); +} + +void uimenu_menu_add(struct uimenu_item_menu *menu, struct uimenu_item_header *where, struct uimenu_item_header *item, bool after) +{ + if (!menu->head) { + /* edge case: menu has no children :((((( */ + menu->head = menu->tail = item; + item->next = item->prev = NULL; + ++menu->nchildren; + return; + } + + umps_assert(where); + + if (after) { + item->prev = where; + item->next = where->next; + where->next = item; + + if (!item->next) menu->tail = item; + } else { + item->next = where; + item->prev = where->prev; + where->prev = item; + + if (!item->prev) menu->head = item; + } + + ++menu->nchildren; +} + +void uimenu__item_free(struct uimenu_item_header *item) +{ + switch (item->type) { + case UMPS__MENU_TYPE_SPACER: + free(item); + break; + case UMPS__MENU_TYPE_BUTTON: { + struct uimenu_item_button *btn = (struct uimenu_item_button *)item; + free(btn->text); + free(btn); + break; + } + case UMPS__MENU_TYPE_MENU: + uimenu_menu_free((struct uimenu_item_menu *)item); + break; + default: + umps_trap; + } +} + +void uimenu_menu_remove(struct uimenu_item_menu *menu, struct uimenu_item_header *item) +{ + if (item->prev) { + item->prev->next = item->next; + } else { + menu->head = item->next; + } + + if (item->next) { + item->next->prev = item->prev; + } else { + menu->tail = item->prev; + } + + --menu->nchildren; +} + +/* frees children */ +void uimenu_menu_free(struct uimenu_item_menu *menu) +{ + for (struct uimenu_item_header *cur = menu->head, *next; cur; cur = next) { + next = cur->next; + uimenu__item_free(cur); + } + free(menu->text); + free(menu); +} diff --git a/src/ui/root.c b/src/ui/root.c index 7ef536f..c3f7468 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -1,5 +1,6 @@ #include "ui.internal.h" #include "macros.h" +#include <curses.h> #define UI__ROOT_MIN_Y (24) #define UI__ROOT_MIN_X (80) @@ -13,7 +14,7 @@ #define UI__ROOT_MARGIN_LEFT (0) #define UI__ROOT_MARGIN_RIGHT (0) -const char *ui__status_text = NULL; +const char *ui__status_text = "Ready"; WINDOW *ui__root_place_content_window(struct ui_window_root *root) { @@ -31,6 +32,9 @@ WINDOW *ui__root_place_content_window(struct ui_window_root *root) return newwin(maxy, maxx, begy, begx); } +void ui__root_draw_menu(struct ui_window_root *root); +void ui__root_draw_status(struct ui_window_root *root); + void ui__root_draw_proc(struct ui_window_base *base) { struct ui_window_root *root = ui__cast(root, base); @@ -47,15 +51,8 @@ void ui__root_draw_proc(struct ui_window_base *base) return; } - attron(A_REVERSE); - mvwhline(base->cwindow, 0, 0, ' ', getmaxx(base->cwindow)); - mvwhline(base->cwindow, getmaxy(base->cwindow)-1, 0, ' ', getmaxx(base->cwindow)); - - if (ui__status_text) { - mvwprintw(base->cwindow, getmaxy(base->cwindow)-1, 1, "Status: %s", ui__status_text); - } - - attroff(A_REVERSE); + ui__root_draw_menu(root); + ui__root_draw_status(root); wnoutrefresh(base->cwindow); @@ -70,6 +67,29 @@ void ui__root_draw_proc(struct ui_window_base *base) doupdate(); } +void ui__root_draw_menu(struct ui_window_root *root) +{ + WINDOW *mywin = root->super.cwindow; + attron(A_REVERSE); + mvwhline(mywin, 0, 0, ' ', getmaxx(mywin)); + mvwaddstr(mywin, 0, 0, " UMPS v0.1.0-dev File Edit Filters Window Help"); + attroff(A_REVERSE); +} + +void ui__root_draw_status(struct ui_window_root *root) +{ + WINDOW *mywin = root->super.cwindow; + + attron(A_REVERSE); + mvwhline(mywin, getmaxy(mywin)-1, 0, ' ', getmaxx(mywin)); + + if (ui__status_text) { + mvwaddstr(mywin, getmaxy(mywin)-1, 1, ui__status_text); + } + + attroff(A_REVERSE); +} + void ui__root_layout_proc(struct ui_window_base *base) { struct ui_window_root *root = ui__cast(root, base); @@ -104,4 +124,9 @@ void ui__root_set_content(struct ui_window_root *root, struct ui_window_base *wi void ui__root_set_floating(struct ui_window_root *root, struct ui_window_base *window) { + umps_assert(!window->parent); + umps_assert(!window->cwindow); + umps_assert(!root->floating); + + } diff --git a/src/ui/ui.internal.h b/src/ui/ui.internal.h index b570654..161391f 100644 --- a/src/ui/ui.internal.h +++ b/src/ui/ui.internal.h @@ -2,6 +2,7 @@ #define UMPS_UI_INTERNAL_H_INCLUDED #include "../ui.h" +#include "uimenu.internal.h" #include "config.h" #include NCURSES_INCLUDE @@ -63,6 +64,8 @@ struct ui_window_root { struct ui_window_base *content; struct ui_window_base *floating; + + struct uimenu_item_menu *menu_root; }; /* internal utils */ diff --git a/src/ui/uimenu.internal.h b/src/ui/uimenu.internal.h new file mode 100644 index 0000000..82360f5 --- /dev/null +++ b/src/ui/uimenu.internal.h @@ -0,0 +1,51 @@ +#ifndef UMPS_UIMENU_INTERNAL_H_INCLUDED +#define UMPS_UIMENU_INTERNAL_H_INCLUDED + +#include <stdbool.h> + +#define UMPS__MENU_TYPE_SPACER (0) +#define UMPS__MENU_TYPE_BUTTON (1) +#define UMPS__MENU_TYPE_MENU (2) + +/* this header is right out of a university data structures slide deck */ + +struct uimenu_item_header { + unsigned type; + struct uimenu_item_header *next, *prev; +}; + +struct uimenu_item_button; + +typedef void (uimenu_button_action)(struct uimenu_item_button *); + +struct uimenu_item_button { + struct uimenu_item_header header; + unsigned id; + char *text; + + uimenu_button_action *action; + + bool enabled; +}; + +struct uimenu_item_menu { + struct uimenu_item_header header; + char *text; + + struct uimenu_item_header *head, *tail; + unsigned nchildren; +}; + +void uimenu_item_button_init(struct uimenu_item_button *button, unsigned id, const char *text, uimenu_button_action *action); +void uimenu_item_menu_init(struct uimenu_item_menu *menu, const char *text); + +/* menu items will be freed on removal */ +void uimenu_menu_add_spacer(struct uimenu_item_menu *menu, struct uimenu_item_header *where, bool after); +void uimenu_menu_add(struct uimenu_item_menu *menu, struct uimenu_item_header *where, struct uimenu_item_header *item, bool after); + +void uimenu_menu_remove(struct uimenu_item_menu *menu, struct uimenu_item_header *item); + +/* frees children */ +void uimenu_menu_free(struct uimenu_item_menu *menu); + +#endif /* include guard */ |
