#pragma once
#include <nanogui/screen.h>
#include <nanogui/glutil.h>
#include <nanogui/toolbutton.h>
#include <map>
#include <memory>
#include <typeinfo>
#include "view.hpp"
#include "module.hpp"
namespace ftl {
namespace gui2 {
/**
* FTL GUI main screen. Methods may only be called from main (GUI) threads
* unless otherwise documented.
*/
class Screen : public nanogui::Screen {
public:
explicit Screen();
virtual ~Screen();
virtual void drawAll() override;
virtual bool keyboardEvent(int key, int scancode, int action, int modifiers) override;
virtual bool keyboardCharacterEvent(unsigned int codepoint) override;
void render(); // necessary?
/** Redraw the screen (triggers an empty event). Thread safe. */
void redraw();
void activate(Module *ptr);
/** set active view (existing object */
void setView(ftl::gui2::View* view);
/** set active view (create new object)*/
template<typename T, typename ... Args>
void setView(Args ... args);
bool isActiveView(View* ptr) { return active_view_ == ptr; }
/** Add a module.*/
template<typename T, typename ... Args>
T* addModule(const std::string &name, ftl::Configurable *config, Args ... args);
/** Get a pointer to module. Module identified by name, exception thrown if not found */
template<typename T>
T* getModule(const std::string &name);
/** Get a pointer to module. Module indentified by dynamic type from template parameter.
* Throws an exception if not found. If more than one possible match (same module
* loaded multiple times), return value can be any.
*/
template<typename T>
T* getModule();
// prever above template (explicit who manages delete)
// template<typename T>
// T* addModule(T* ptr) { return addModule_(ptr); }
// TODO removeModule() as well?
/** add a button to toolbar */
template<typename T=nanogui::ToolButton, typename ... Args>
T* addButton(Args ... args);
/** themes/colors */
nanogui::Theme* getTheme(const std::string &name);
nanogui::Color getColor(const std::string &name);
void setColor(const std::string &name, const nanogui::Color &c);
// Implement in View or Screen? Add ID (address of creating instance)
// to each error to prevent spam?
/** Show error message popup */
void showError(const std::string& title, const std::string &msg);
nanogui::Vector2i viewSize(const nanogui::Vector2i &ws);
nanogui::Vector2i viewSize();
private:
Module* addModule_(const std::string &name, Module* ptr);
//std::mutex mtx_; // not used: do not modify gui outside gui (main) thread
std::map<std::string, ftl::gui2::Module*> modules_;
std::map<std::string, nanogui::ref<nanogui::Theme>> themes_;
std::map<std::string, nanogui::Color> colors_;
nanogui::Widget *toolbar_;
nanogui::Widget *tools_;
ftl::gui2::View *active_view_;
nanogui::MessageDialog* msgerror_;
double last_draw_time_=0.0f;
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
template<typename T, typename ... Args>
void Screen::setView(Args ... args) {
setView(new T(this, args ...));
}
template<typename T, typename ... Args>
T* Screen::addModule(const std::string &name, ftl::Configurable *config, Args ... args) {
static_assert(std::is_base_of<Module, T>::value);
return dynamic_cast<T*>(
addModule_(
name,
ftl::config::create<T>(config, name, args ...)
)
);
}
template<typename T>
T* Screen::getModule(const std::string &name) {
static_assert(std::is_base_of<Module, T>::value);
if (modules_.find(name) == modules_.end()) {
throw ftl::exception("module: " + name + " not found");
}
auto* ptr = dynamic_cast<T*>(modules_[name]);
if (ptr == nullptr) {
throw ftl::exception("bad cast, module requested with wrong type");
}
return ptr;
}
template<typename T>
T* Screen::getModule() {
static_assert(std::is_base_of<Module, T>::value);
for (auto& [name, ptr] : modules_) {
std::ignore = name;
if (typeid(*ptr) == typeid(T)) {
return dynamic_cast<T*>(ptr);
}
}
throw ftl::exception("module not found");
}
template<typename T, typename ... Args>
T* Screen::addButton(Args ... args) {
static_assert(std::is_base_of<nanogui::Button, T>::value);
T* button = new T(tools_, args ...);
button->setIconExtraScale(1.5f);
button->setTheme(themes_["toolbutton"]);
button->setFixedSize(nanogui::Vector2i(40, 40));
performLayout();
return button;
}
}
}