From 3b12ff93a3b7f409f88f96187fdf0bd776368bd2 Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Sat, 14 Jun 2014 22:00:53 +0100 Subject: [PATCH] refs #248 C++ API wrapper for the X-Plane Plugin SDK's C API for creating menus --- src/xbus/menus.cpp | 122 +++++++++++++++++++++++++++++++++++++++++++++ src/xbus/menus.h | 100 +++++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 src/xbus/menus.cpp create mode 100644 src/xbus/menus.h diff --git a/src/xbus/menus.cpp b/src/xbus/menus.cpp new file mode 100644 index 000000000..e004755a1 --- /dev/null +++ b/src/xbus/menus.cpp @@ -0,0 +1,122 @@ +/* Copyright (C) 2013 VATSIM Community / contributors + * 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 "menus.h" +#include +#include + +namespace XBus +{ + + // Own implementation of std::make_unique, a C++14 feature not provided by GCC in C++11 mode + template + std::unique_ptr make_unique(Args&&... args) + { + return std::unique_ptr(new T(std::forward(args)...)); + } + + template void *voidptr_cast(T i) // "safe" cast from integer to void* + { + static_assert(std::is_integral::value, "voidptr_cast expects an integer"); + typedef typename std::conditional::value, intptr_t, uintptr_t>::type intptr_type; + return reinterpret_cast(static_cast(i)); + } + + template T intptr_cast(void *p) // "safe" cast from void* to integer + { + static_assert(std::is_integral::value, "voidptr_cast returns an integer"); + typedef typename std::conditional::value, intptr_t, uintptr_t>::type intptr_type; + return static_cast(reinterpret_cast(p)); + } + + CMenu::CMenu(XPLMMenuID id, bool isMainMenu, std::unique_ptr items) + : m_data(std::make_shared(id, isMainMenu, std::move(items))) + {} + + CMenu::Data::~Data() + { + if (! isMainMenu) + { + XPLMDestroyMenu(id); + } + } + + CMenu CMenu::mainMenu() + { + return { XPLMFindPluginsMenu(), true, nullptr }; + } + + CMenuItem CMenu::item(std::string name, std::function callback) + { + assert(! name.empty()); + m_data->items->emplace_back( + CMenuItem { m_data->id, XPLMAppendMenuItem(m_data->id, name.c_str(), voidptr_cast(m_data->items->size() + 1), false), false, false }, + [callback](bool){ callback(); } + ); + return m_data->items->back().first; + } + + CMenuItem CMenu::checkableItem(std::string name, bool checked, std::function callback) + { + assert(! name.empty()); + m_data->items->emplace_back( + CMenuItem { m_data->id, XPLMAppendMenuItem(m_data->id, name.c_str(), voidptr_cast(m_data->items->size() + 1), false), true, checked }, + callback + ); + return m_data->items->back().first; + } + + void CMenu::sep() + { + XPLMAppendMenuSeparator(m_data->id); + } + + CMenu CMenu::subMenu(std::string name) + { + assert(! name.empty()); + auto items = make_unique(); + auto itemsVoidPtr = static_cast(&*items); + return { XPLMCreateMenu(name.c_str(), m_data->id, XPLMAppendMenuItem(m_data->id, name.c_str(), nullptr, false), handler, itemsVoidPtr), false, std::move(items) }; + } + + void CMenu::handler(void *menuRef, void *itemRef) + { + if (menuRef && itemRef) + { + auto items = static_cast(menuRef); + auto itemIdx = intptr_cast(itemRef) - 1; + assert(itemIdx >= 0); + + (*items)[itemIdx].second((*items)[itemIdx].first.getChecked()); + } + } + + CMenuItem::CMenuItem(XPLMMenuID parent, int item, bool checkable, bool checked) + : m_data(std::make_shared(parent, item, checkable)) + { + if (checkable) + { + setChecked(checked); + } + } + + bool CMenuItem::getChecked() const + { + XPLMMenuCheck check = xplm_Menu_NoCheck; + XPLMCheckMenuItemState(m_data->parent, m_data->item, &check); + return check == xplm_Menu_Checked; + } + + void CMenuItem::setChecked(bool checked) + { + XPLMCheckMenuItem(m_data->parent, m_data->item, checked); + } + + void CMenuItem::setEnabled(bool enabled) + { + XPLMEnableMenuItem(m_data->parent, m_data->item, enabled); + } + +} diff --git a/src/xbus/menus.h b/src/xbus/menus.h new file mode 100644 index 000000000..eea98ef4c --- /dev/null +++ b/src/xbus/menus.h @@ -0,0 +1,100 @@ +/* Copyright (C) 2013 VATSIM Community / contributors + * 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 BLACKSIM_XBUS_MENUS_H +#define BLACKSIM_XBUS_MENUS_H + +//! \file + +#include +#include +#include +#include + +namespace XBus +{ + + class CMenu; + + /*! + * Class-based interface to X-Plane SDK menu items. + */ + class CMenuItem + { + public: + //! Construct an uninitialized menu item object + CMenuItem() = default; + + //! Returns true if the menu item is checked + bool getChecked() const; + + //! Sets the checked status of the menu item + void setChecked(bool checked); + + //! Enables or disabled the menu item + void setEnabled(bool enabled); + + private: + friend class CMenu; + + CMenuItem(XPLMMenuID parent, int item, bool checkable, bool checked); + + struct Data + { + Data(XPLMMenuID parent_, int item_, bool checkable_) : parent(parent_), item(item_), checkable(checkable_) {} + XPLMMenuID parent; + int item; + bool checkable; + }; + std::shared_ptr m_data; + }; + + /*! + * Class-based interface to X-Plane SDK menus. + */ + class CMenu + { + public: + //! Construct an uninitialized menu object + CMenu() = default; + + //! Returns a menu object representing the top-level menu of all plugins. + static CMenu mainMenu(); + + //! Appends an item to the menu and returns it. + CMenuItem item(std::string name, std::function callback); + + //! Appends a checkbox item to the menu and returns it. + CMenuItem checkableItem(std::string name, bool checked, std::function callback); + + //! Appends a separator to the menu. + void sep(); + + //! Appends an item to the menu which opens a sub-menu, and returns it. + CMenu subMenu(std::string name); + + private: + typedef std::vector>> ItemList; + + CMenu(XPLMMenuID id, bool isMainMenu, std::unique_ptr callbacks); + + static void handler(void *menuRef, void *itemRef); + + struct Data + { + Data(XPLMMenuID id_, bool isMainMenu_, std::unique_ptr items_) + : id(id_), isMainMenu(isMainMenu_), items(std::move(items_)) {} + XPLMMenuID id; + bool isMainMenu; + std::unique_ptr items; + std::vector subMenus; + ~Data(); + }; + std::shared_ptr m_data; + }; + +} + +#endif // guard