From c21e0917a5a1650f0350faf88d8d1800beee6e50 Mon Sep 17 00:00:00 2001 From: Nicolas Pope <nicolas.pope@utu.fi> Date: Thu, 9 Jan 2020 10:50:25 +0200 Subject: [PATCH] Add a search bar The configuration window now has a search bar which reacts to user input on the fly. --- applications/gui/src/config_window.cpp | 106 +++++++++++++++++- applications/gui/src/config_window.hpp | 5 +- applications/reconstruct/src/main.cpp | 7 ++ .../common/cpp/include/ftl/configuration.hpp | 7 ++ components/common/cpp/src/configuration.cpp | 11 ++ 5 files changed, 129 insertions(+), 7 deletions(-) diff --git a/applications/gui/src/config_window.cpp b/applications/gui/src/config_window.cpp index 322f817ae..b3c5ef874 100644 --- a/applications/gui/src/config_window.cpp +++ b/applications/gui/src/config_window.cpp @@ -6,6 +6,7 @@ #include <nanogui/entypo.h> #include <nanogui/formhelper.h> #include <nanogui/vscrollpanel.h> +#include <nanogui/opengl.h> #include <vector> #include <string> @@ -15,6 +16,67 @@ using std::string; using std::vector; using ftl::config::json_t; +class SearchBox : public nanogui::TextBox { +private: + std::vector<std::string> configurables_; + Widget *buttons_; + std::string previous; + + void _setVisible(const std::string &str) { + // Check whether the search string has changed to prevent + // unnecessary searching. + if (str != previous) { + for (int i = configurables_.size()-1; i >= 0; --i) { + if (configurables_[i].find(mValueTemp) != std::string::npos) { + buttons_->childAt(i)->setVisible(true); + } else { + buttons_->childAt(i)->setVisible(false); + } + } + previous = str; + } + } + +public: + SearchBox(Widget *parent, std::vector<std::string> &configurables) : nanogui::TextBox(parent, ""), configurables_(configurables) { + setAlignment(TextBox::Alignment::Left); + setEditable(true); + setPlaceholder("Search"); + } + + ~SearchBox() { + } + + bool keyboardEvent(int key, int scancode, int action, int modifier) { + TextBox::keyboardEvent(key, scancode, action, modifier); + _setVisible(mValueTemp); + return true; + } + + void setButtons(Widget *buttons) { + buttons_ = buttons; + } +}; + +static std::string titleForURI(const ftl::URI &uri) { + auto *cfg = ftl::config::find(uri.getBaseURI()); + if (cfg && cfg->get<std::string>("title")) { + return *cfg->get<std::string>("title"); + } else if (uri.getPath().size() > 0) { + return uri.getPathSegment(-1); + } else { + return uri.getHost(); + } +} + +static std::string genPadding(const std::string &str, size_t count) { + std::string res = ""; + for (size_t i=0; i<count; ++i) { + res += str; + } + return res; +} + ConfigWindow::ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl) : nanogui::Window(parent, "Settings"), ctrl_(ctrl) { using namespace nanogui; @@ -23,17 +85,52 @@ ConfigWindow::ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl) setPosition(Vector2i(parent->width()/2.0f - 100.0f, parent->height()/2.0f - 100.0f)); //setModal(true); - configurables_ = ftl::config::list(); + auto configurables = ftl::config::list(); + const auto size = configurables.size(); new Label(this, "Select Configurable","sans-bold"); + auto searchBox = new SearchBox(this, configurables); + auto vscroll = new VScrollPanel(this); vscroll->setFixedHeight(300); - Widget *buttons = new Widget(vscroll); + auto buttons = new Widget(vscroll); buttons->setLayout(new BoxLayout(Orientation::Vertical, Alignment::Fill)); - for (auto c : configurables_) { - auto itembutton = new Button(buttons, c); + searchBox->setButtons(buttons); + + std::vector<std::string> configurable_titles(size); + for (int i = 0; i < size; ++i) { + ftl::URI uri(configurables[i]); + std::string label = uri.getFragment(); + + size_t pos = label.find_last_of("/"); + if (pos != std::string::npos) label = label.substr(pos+1); + + std::string parentName = configurables[i]; + size_t pos2 = parentName.find_last_of("/"); + if (pos2 != std::string::npos) parentName = parentName.substr(0,pos2); + + // FIXME: Does not indicated parent indentation ... needs sorting? + + if (i > 0 && parentName == configurables[i-1]) { + ftl::URI uri(configurables[i-1]); + configurable_titles[i-1] = std::string("[") + titleForURI(uri) + std::string("] ") + uri.getFragment(); + + auto *prev = dynamic_cast<Button*>(buttons->childAt(buttons->childCount()-1)); + prev->setCaption(configurable_titles[i-1]); + prev->setBackgroundColor(nanogui::Color(0.3f,0.3f,0.3f,1.0f)); + prev->setTextColor(nanogui::Color(1.0f,1.0f,1.0f,1.0f)); + prev->setIconPosition(Button::IconPosition::Left); + prev->setIcon(ENTYPO_ICON_FOLDER); + } + + configurable_titles[i] = label; + + auto itembutton = new nanogui::Button(buttons, configurable_titles[i]); + std::string c = configurables[i]; + itembutton->setTooltip(c); + itembutton->setBackgroundColor(nanogui::Color(0.9f,0.9f,0.9f,0.9f)); itembutton->setCallback([this,c]() { LOG(INFO) << "Change configurable: " << c; _buildForm(c); @@ -136,3 +233,4 @@ void ConfigWindow::_buildForm(const std::string &suri) { bool ConfigWindow::exists(const std::string &uri) { return ftl::config::find(uri) != nullptr; } + diff --git a/applications/gui/src/config_window.hpp b/applications/gui/src/config_window.hpp index a0fe74155..a7acd1171 100644 --- a/applications/gui/src/config_window.hpp +++ b/applications/gui/src/config_window.hpp @@ -2,10 +2,10 @@ #define _FTL_GUI_CFGWINDOW_HPP_ #include <nanogui/window.h> +#include <nanogui/formhelper.h> + #include <ftl/master.hpp> #include <ftl/uuid.hpp> - -#include <nanogui/formhelper.h> #include <ftl/net_configurable.hpp> namespace ftl { @@ -21,7 +21,6 @@ class ConfigWindow : public nanogui::Window { private: ftl::ctrl::Master *ctrl_; - std::vector<std::string> configurables_; void _buildForm(const std::string &uri); void _addElements(nanogui::FormHelper *form, const std::string &suri); diff --git a/applications/reconstruct/src/main.cpp b/applications/reconstruct/src/main.cpp index c3dc37e32..5cce7a5a2 100644 --- a/applications/reconstruct/src/main.cpp +++ b/applications/reconstruct/src/main.cpp @@ -227,6 +227,13 @@ static void run(ftl::Configurable *root) { }); stream->add(vs); + for (auto c : ftl::config::getChildren(root->getID())) { + LOG(INFO) << "Tagging configurable: " << c->getID(); + auto tags = c->value<std::vector<std::string>>("tags", nlohmann::json::array({})); + tags.push_back("reconstruction"); + c->set("tags", tags); + } + // ---- Recording code ----------------------------------------------------- std::ofstream fileout; ftl::codecs::Writer writer(fileout); diff --git a/components/common/cpp/include/ftl/configuration.hpp b/components/common/cpp/include/ftl/configuration.hpp index 18aaf89f9..ed7eee95d 100644 --- a/components/common/cpp/include/ftl/configuration.hpp +++ b/components/common/cpp/include/ftl/configuration.hpp @@ -83,6 +83,13 @@ const std::vector<Configurable *> &findByTag(const std::string &tag); std::vector<std::string> list(); +/** + * Recursively get all children of a configurable. The given configurable is + * also included in the vector, unless it is null, + * in which case an empty vector is returned. + */ +const std::vector<Configurable *> getChildren(const std::string &uri); + /** * Adds a Configurable instance to the database of instances so that it can * then be resolved using find(). diff --git a/components/common/cpp/src/configuration.cpp b/components/common/cpp/src/configuration.cpp index 9fd9d3081..c53f50c78 100644 --- a/components/common/cpp/src/configuration.cpp +++ b/components/common/cpp/src/configuration.cpp @@ -212,6 +212,17 @@ std::vector<std::string> ftl::config::list() { return r; } +const std::vector<Configurable *> ftl::config::getChildren(const string &uri) { + std::vector<Configurable *> children; + for (const auto &[curi, c] : config_instance) { + auto mismatch = std::mismatch(uri.begin(), uri.end(), curi.begin()); + if (mismatch.first == uri.end()) { + children.push_back(c); + } + } + return children; +} + void ftl::config::registerConfigurable(ftl::Configurable *cfg) { auto uri = cfg->get<string>("$id"); if (!uri) { -- GitLab