#include "menus.hpp" #ifdef __APPLE__ #define IS_MACOSX true #else #define IS_MACOSX false #endif namespace LooperUI { static std::unordered_map menu_type_data { {MenuType_Default, true}, {MenuType_Item, true}, {MenuType_Application, IS_MACOSX}, {MenuType_Window, IS_MACOSX}, {MenuType_Help, true}, {MenuType_Services, IS_MACOSX} }; bool MenuTypeValidForSystem(MenuType type) { if (menu_type_data.contains(type)) { return menu_type_data[type]; } else { return false; } } MenuItemBase::MenuItemBase(std::string title) : title(title) , type(MenuType_Default) { this->enabled = true; this->visible = true; } MenuItemBase::MenuItemBase(MenuType type) : MenuItemBase("") { this->type = type; } MenuItem::MenuItem(std::string title, std::string keybindString, std::function callback, void *userdata) : MenuItemBase(title) { this->keybindString = keybindString; this->userdata = userdata; this->callback = callback; this->type = MenuType_Item; } Menu::iterator Menu::begin() { return children.begin(); } Menu::iterator Menu::end() { return children.end(); } Menu::const_iterator Menu::cbegin() const { return children.cbegin(); } Menu::const_iterator Menu::cend() const { return children.cend(); } Menu::reverse_iterator Menu::rbegin() { return children.rbegin(); } Menu::reverse_iterator Menu::rend() { return children.rend(); } Menu::const_reverse_iterator Menu::crbegin() const { return children.crbegin(); } Menu::const_reverse_iterator Menu::crend() const { return children.crend(); } MenuItemBase *Menu::operator[](size_t index) { return children[index]; } void Menu::Prepend(MenuItemBase *menu) { children.insert(children.begin(), menu); } void Menu::Insert(MenuItemBase *menu, size_t index) { if (index >= children.size()) { Append(menu); } else { children.insert(children.begin() + index, menu); } } bool MenuItemBase::IsSubmenu() { return false; } bool Menu::IsSubmenu() { return true; } Menu::Menu(std::string title, MenuType type) : MenuItemBase(title) { this->type = type; } bool MenuItemBase::HasParent() { return parent != nullptr; } Menu *MenuItemBase::GetParent() { return parent; } void Menu::Append(MenuItemBase *menu) { if (menu->parent != nullptr) { throw std::exception(); } menu->parent = this; children.push_back(menu); } MenuBuilder &MenuBuilder::BeginSubmenu(std::string title, MenuType type, OSOptions allowedOS) { throwIfBuilt(); Menu *submenu = new Menu(title); submenu->os_options = allowedOS; submenu->type = type; current_menu->Append(submenu); current_menu = submenu; return *this; } MenuBuilder &MenuBuilder::EndSubmenu() { throwIfBuilt(); if (current_menu->HasParent()) { current_menu = current_menu->GetParent(); } else { throw std::exception(); } return *this; } MenuBuilder &MenuBuilder::AddEmptySubmenu(std::string title, MenuType type, OSOptions allowedOS) { BeginSubmenu(title, type, allowedOS).EndSubmenu(); return *this; } MenuBuilder &MenuBuilder::AddItem(std::string title, std::string keybindString, std::function callback, void *userdata, OSOptions allowedOS) { throwIfBuilt(); MenuItem *newItem = new MenuItem(title, keybindString, callback, userdata); newItem->os_options = allowedOS; current_menu->Append(newItem); return *this; } MenuBuilder &MenuBuilder::AddSeparator(OSOptions allowedOS) { throwIfBuilt(); MenuItemBase *newItem = new MenuItemBase(MenuType_Separator); newItem->os_options = allowedOS; current_menu->Append(newItem); return *this; } MenuBuilder &MenuBuilder::AddItemNoUserdata(std::string title, std::string keybindString, std::function callback_no_userdata, OSOptions allowedOS) { AddItem(title, keybindString, [callback_no_userdata](MenuItem *item, void*) { callback_no_userdata(item); }, nullptr, allowedOS); return *this; } MenuBuilder &MenuBuilder::AddItemSimple(std::string title, std::string keybindString, std::function callback_simple, OSOptions allowedOS) { AddItem(title, keybindString, [callback_simple](MenuItem*, void*) { callback_simple(); }, nullptr, allowedOS); return *this; } MenuBuilder::MenuBuilder(std::string rootTitle) { root = new Menu(rootTitle); current_menu = root; built = false; } Menu *MenuBuilder::Build() { throwIfBuilt(); built = true; return root; } void MenuBuilder::throwIfBuilt() { if (built) throw std::exception(); } static void _IterateMenu(Menu *menu, MenuIteratorCallbacks &callbacks) { for (MenuItemBase *item : *menu) { bool allowed = (item->os_options == OSOptions::AnyOS) || (item->os_options == #ifdef __APPLE__ OSOptions::OnlyMac #else OSOptions::ExcludeMac #endif ); if (!allowed) continue; if (item->type == MenuType_Separator) { callbacks.AddSeparator(); } else if (item->IsSubmenu()) { callbacks.BeginSubmenu((Menu*)item); _IterateMenu((Menu*)item, callbacks); callbacks.EndSubmenu(); } else { callbacks.AddNormalItem((MenuItem*)item); } } } void IterateMenu(Menu *menu, MenuIteratorCallbacks &callbacks) { callbacks.Init(menu); _IterateMenu(menu, callbacks); callbacks.Finalize(); } MenuItemBase *FindMenu(Menu *menu, std::string title) { for (MenuItemBase *item : *menu) { bool allowed = item->os_options == OSOptions::AnyOS || item->os_options == #ifdef __APPLE__ OSOptions::OnlyMac #else OSOptions::ExcludeMac #endif ; if (!allowed) continue; if (item->title == title) { return item; } else if (item->IsSubmenu()) { MenuItemBase *maybe_output = FindMenu((Menu*)item, title); if (maybe_output != nullptr) { return maybe_output; } } } return nullptr; } }