looper/liblooperui/menus.cpp
Zachary Hall 51d4ed39ab
Some checks failed
Build / build-gentoo (push) Failing after 14s
Build / download-system-deps (push) Successful in 3m28s
Build / get-source-code (push) Successful in 9m18s
Build / build-deb (push) Failing after 3m41s
Build / build-appimage (push) Successful in 4m12s
Build / build-android (push) Failing after 2m47s
Build / build-windows (push) Failing after 7m10s
Add support for macOS
- Remove unused OpenMP from playback backend
  - Fix update_assets on macOS
  - Add support for Objective C++ on macOS
  - Add macOS-specific code into the Dear ImGui backend
  - Add macOS .app packaging
  - Add support for global menus, and include support for macOS global menu into the Dear ImGui backend
2025-02-09 10:13:46 -08:00

213 lines
6 KiB
C++

#include "menus.hpp"
#ifdef __APPLE__
#define IS_MACOSX true
#else
#define IS_MACOSX false
#endif
namespace LooperUI {
static std::unordered_map<MenuType, bool> 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<void(MenuItem*, void*)> 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<void(MenuItem*, void*)> 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<void(MenuItem*)> 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<void()> 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;
}
}