all repos — mgba @ 31f40b05ba0ca4be6bc0a1988ef81cac3e49307f

mGBA Game Boy Advance Emulator

GUI: Menu abstraction
Jeffrey Pfau jeffrey@endrift.com
Sun, 30 Aug 2015 13:14:29 -0700
commit

31f40b05ba0ca4be6bc0a1988ef81cac3e49307f

parent

1c351d92c74faf2ed8ebbdc493de359383a79414

4 files changed, 158 insertions(+), 94 deletions(-)

jump to
M src/util/gui.hsrc/util/gui.h

@@ -11,7 +11,6 @@

#include "util/vector.h" struct GUIFont; -DECLARE_VECTOR(FileList, char*); enum GUIInput { GUI_INPUT_NONE = -1,
M src/util/gui/file-select.csrc/util/gui/file-select.c

@@ -6,22 +6,21 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "file-select.h" #include "util/gui/font.h" +#include "util/gui/menu.h" #include "util/vfs.h" #include <stdlib.h> - -DEFINE_VECTOR(FileList, char*); #define ITERATION_SIZE 5 #define SCANNING_THRESHOLD 15 -static void _cleanFiles(struct FileList* currentFiles) { - size_t size = FileListSize(currentFiles); +static void _cleanFiles(struct GUIMenuItemList* currentFiles) { + size_t size = GUIMenuItemListSize(currentFiles); size_t i; for (i = 1; i < size; ++i) { - free(*FileListGetPointer(currentFiles, i)); + free(GUIMenuItemListGetPointer(currentFiles, i)->title); } - FileListClear(currentFiles); + GUIMenuItemListClear(currentFiles); } static void _upDirectory(char* currentPath) {

@@ -41,17 +40,17 @@ // TODO: What if there was a trailing slash?

} static int _strpcmp(const void* a, const void* b) { - return strcmp(*(const char**) a, *(const char**) b); + return strcmp(((const struct GUIMenuItem*) a)->title, ((const struct GUIMenuItem*) b)->title); } -static bool _refreshDirectory(struct GUIParams* params, const char* currentPath, struct FileList* currentFiles, bool (*filter)(struct VFile*)) { +static bool _refreshDirectory(struct GUIParams* params, const char* currentPath, struct GUIMenuItemList* currentFiles, bool (*filter)(struct VFile*)) { _cleanFiles(currentFiles); struct VDir* dir = VDirOpen(currentPath); if (!dir) { return false; } - *FileListAppend(currentFiles) = "(Up)"; + *GUIMenuItemListAppend(currentFiles) = (struct GUIMenuItem) { .title = "(Up)" }; size_t i = 0; struct VDirEntry* de; while ((de = dir->listNext(dir))) {

@@ -64,7 +63,7 @@ return false;

} params->drawStart(); GUIFontPrintf(params->font, 0, GUIFontHeight(params->font), GUI_TEXT_LEFT, 0xFFFFFFFF, "%s", currentPath); - GUIFontPrintf(params->font, 0, GUIFontHeight(params->font) * 2, GUI_TEXT_LEFT, 0xFFFFFFFF, "(scanning)"); + GUIFontPrintf(params->font, 0, GUIFontHeight(params->font) * 2, GUI_TEXT_LEFT, 0xFFFFFFFF, "(scanning item %z)", i); params->drawEnd(); } const char* name = de->name(de);

@@ -77,70 +76,38 @@ if (!vf) {

continue; } if (!filter || filter(vf)) { - *FileListAppend(currentFiles) = strdup(name); + *GUIMenuItemListAppend(currentFiles) = (struct GUIMenuItem) { .title = strdup(name) }; } vf->close(vf); } else { - *FileListAppend(currentFiles) = strdup(name); + *GUIMenuItemListAppend(currentFiles) = (struct GUIMenuItem) { .title = strdup(name) }; } } dir->close(dir); - qsort(FileListGetPointer(currentFiles, 1), FileListSize(currentFiles) - 1, sizeof(char*), _strpcmp); + qsort(GUIMenuItemListGetPointer(currentFiles, 1), GUIMenuItemListSize(currentFiles) - 1, sizeof(struct GUIMenuItem), _strpcmp); + return true; } bool GUISelectFile(struct GUIParams* params, char* outPath, size_t outLen, bool (*filter)(struct VFile*)) { - size_t start = 0; - size_t pageSize = params->height / GUIFontHeight(params->font); - if (pageSize > 4) { - pageSize -= 4; - } else { - pageSize = 1; - } - - GUIInvalidateKeys(params); - struct FileList currentFiles; - FileListInit(&currentFiles, 0); - _refreshDirectory(params, params->currentPath, &currentFiles, filter); + struct GUIMenu menu = { + .title = params->currentPath, + .index = params->fileIndex, + }; + GUIMenuItemListInit(&menu.items, 0); + _refreshDirectory(params, params->currentPath, &menu.items, filter); while (true) { - int newInput = 0; - GUIPollInput(params, &newInput, 0); - - if (newInput & (1 << GUI_INPUT_UP) && params->fileIndex > 0) { - --params->fileIndex; - } - if (newInput & (1 << GUI_INPUT_DOWN) && params->fileIndex < FileListSize(&currentFiles) - 1) { - ++params->fileIndex; - } - if (newInput & (1 << GUI_INPUT_LEFT)) { - if (params->fileIndex >= pageSize) { - params->fileIndex -= pageSize; - } else { - params->fileIndex = 0; - } - } - if (newInput & (1 << GUI_INPUT_RIGHT)) { - if (params->fileIndex + pageSize < FileListSize(&currentFiles)) { - params->fileIndex += pageSize; - } else { - params->fileIndex = FileListSize(&currentFiles) - 1; - } - } - - if (params->fileIndex < start) { - start = params->fileIndex; - } - while ((params->fileIndex - start + 4) * GUIFontHeight(params->font) > params->height) { - ++start; - } - if (newInput & (1 << GUI_INPUT_CANCEL)) { + struct GUIMenuItem item; + enum GUIMenuExitReason reason = GUIShowMenu(params, &menu, &item); + params->fileIndex = menu.index; + if (reason == GUI_MENU_EXIT_CANCEL) { break; } - if (newInput & (1 << GUI_INPUT_SELECT)) { + if (reason == GUI_MENU_EXIT_ACCEPT) { if (params->fileIndex == 0) { _upDirectory(params->currentPath); - if (!_refreshDirectory(params, params->currentPath, &currentFiles, filter)) { + if (!_refreshDirectory(params, params->currentPath, &menu.items, filter)) { break; } } else {

@@ -149,69 +116,47 @@ const char* sep = PATH_SEP;

if (params->currentPath[len - 1] == *sep) { sep = ""; } - snprintf(outPath, outLen, "%s%s%s", params->currentPath, sep, *FileListGetPointer(&currentFiles, params->fileIndex)); + snprintf(outPath, outLen, "%s%s%s", params->currentPath, sep, item.title); - struct FileList newFiles; - FileListInit(&newFiles, 0); + struct GUIMenuItemList newFiles; + GUIMenuItemListInit(&newFiles, 0); if (!_refreshDirectory(params, outPath, &newFiles, filter)) { _cleanFiles(&newFiles); - FileListDeinit(&newFiles); + GUIMenuItemListDeinit(&newFiles); struct VFile* vf = VFileOpen(outPath, O_RDONLY); if (!vf) { continue; } if (!filter || filter(vf)) { vf->close(vf); - _cleanFiles(&currentFiles); - FileListDeinit(&currentFiles); + _cleanFiles(&menu.items); + GUIMenuItemListDeinit(&menu.items); return true; } vf->close(vf); break; } else { - _cleanFiles(&currentFiles); - FileListDeinit(&currentFiles); - currentFiles = newFiles; + _cleanFiles(&menu.items); + GUIMenuItemListDeinit(&menu.items); + menu.items = newFiles; strncpy(params->currentPath, outPath, PATH_MAX); } } params->fileIndex = 0; } - if (newInput & (1 << GUI_INPUT_BACK)) { + if (reason == GUI_MENU_EXIT_BACK) { if (strncmp(params->currentPath, params->basePath, PATH_MAX) == 0) { break; } _upDirectory(params->currentPath); - if (!_refreshDirectory(params, params->currentPath, &currentFiles, filter)) { + if (!_refreshDirectory(params, params->currentPath, &menu.items, filter)) { break; } params->fileIndex = 0; } - - params->drawStart(); - unsigned y = GUIFontHeight(params->font); - GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, 0xFFFFFFFF, "%s", params->currentPath); - y += 2 * GUIFontHeight(params->font); - size_t i; - for (i = start; i < FileListSize(&currentFiles); ++i) { - int color = 0xE0A0A0A0; - char bullet = ' '; - if (i == params->fileIndex) { - color = 0xFFFFFFFF; - bullet = '>'; - } - GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, color, "%c %s", bullet, *FileListGetPointer(&currentFiles, i)); - y += GUIFontHeight(params->font); - if (y + GUIFontHeight(params->font) > params->height) { - break; - } - } - y += GUIFontHeight(params->font) * 2; - - params->drawEnd(); } - _cleanFiles(&currentFiles); - FileListDeinit(&currentFiles); + _cleanFiles(&menu.items); + GUIMenuItemListDeinit(&menu.items); return false; }
A src/util/gui/menu.c

@@ -0,0 +1,88 @@

+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "menu.h" + +#include "util/gui.h" +#include "util/gui/font.h" + +DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem); + +enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* menu, struct GUIMenuItem* item) { + size_t start = 0; + size_t pageSize = params->height / GUIFontHeight(params->font); + if (pageSize > 4) { + pageSize -= 4; + } else { + pageSize = 1; + } + + GUIInvalidateKeys(params); + while (true) { + int newInput = 0; + GUIPollInput(params, &newInput, 0); + + if (newInput & (1 << GUI_INPUT_UP) && menu->index > 0) { + --menu->index; + } + if (newInput & (1 << GUI_INPUT_DOWN) && menu->index < GUIMenuItemListSize(&menu->items) - 1) { + ++menu->index; + } + if (newInput & (1 << GUI_INPUT_LEFT)) { + if (menu->index >= pageSize) { + menu->index -= pageSize; + } else { + menu->index = 0; + } + } + if (newInput & (1 << GUI_INPUT_RIGHT)) { + if (menu->index + pageSize < GUIMenuItemListSize(&menu->items)) { + menu->index += pageSize; + } else { + menu->index = GUIMenuItemListSize(&menu->items) - 1; + } + } + + if (menu->index < start) { + start = menu->index; + } + while ((menu->index - start + 4) * GUIFontHeight(params->font) > params->height) { + ++start; + } + if (newInput & (1 << GUI_INPUT_CANCEL)) { + break; + } + if (newInput & (1 << GUI_INPUT_SELECT)) { + *item = *GUIMenuItemListGetPointer(&menu->items, menu->index); + return GUI_MENU_EXIT_ACCEPT; + } + if (newInput & (1 << GUI_INPUT_BACK)) { + return GUI_MENU_EXIT_BACK; + } + + params->drawStart(); + unsigned y = GUIFontHeight(params->font); + GUIFontPrint(params->font, 0, y, GUI_TEXT_LEFT, 0xFFFFFFFF, menu->title); + y += 2 * GUIFontHeight(params->font); + size_t i; + for (i = start; i < GUIMenuItemListSize(&menu->items); ++i) { + int color = 0xE0A0A0A0; + char bullet = ' '; + if (i == menu->index) { + color = 0xFFFFFFFF; + bullet = '>'; + } + GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, color, "%c %s", bullet, GUIMenuItemListGetPointer(&menu->items, i)->title); + y += GUIFontHeight(params->font); + if (y + GUIFontHeight(params->font) > params->height) { + break; + } + } + y += GUIFontHeight(params->font) * 2; + + params->drawEnd(); + } + return GUI_MENU_EXIT_CANCEL; +}
A src/util/gui/menu.h

@@ -0,0 +1,32 @@

+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef GUI_MENU_H +#define GUI_MENU_H + +#include "util/vector.h" + +struct GUIMenuItem { + const char* title; + void* data; +}; + +DECLARE_VECTOR(GUIMenuItemList, struct GUIMenuItem); +struct GUIMenu { + const char* title; + struct GUIMenuItemList items; + size_t index; +}; + +enum GUIMenuExitReason { + GUI_MENU_EXIT_ACCEPT, + GUI_MENU_EXIT_BACK, + GUI_MENU_EXIT_CANCEL, +}; + +struct GUIParams; +enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* menu, struct GUIMenuItem* item); + +#endif