diff options
Diffstat (limited to 'src/ui/dialog/command-palette.h')
-rw-r--r-- | src/ui/dialog/command-palette.h | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/src/ui/dialog/command-palette.h b/src/ui/dialog/command-palette.h new file mode 100644 index 0000000..e0316ac --- /dev/null +++ b/src/ui/dialog/command-palette.h @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * CommandPalette: Class providing Command Palette feature + * + * Authors: + * Abhay Raj Singh <abhayonlyone@gmail.com> + * + * Copyright (C) 2020 Autors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#ifndef INKSCAPE_DIALOG_COMMAND_PALETTE_H +#define INKSCAPE_DIALOG_COMMAND_PALETTE_H + +#include <utility> +#include <vector> + +#include <giomm/action.h> +#include <giomm/application.h> +#include <glibmm/refptr.h> +#include <glibmm/ustring.h> +#include <gtkmm/box.h> +#include <gtkmm/builder.h> +#include <gtkmm/button.h> +#include <gtkmm/eventbox.h> +#include <gtkmm/label.h> +#include <gtkmm/listbox.h> +#include <gtkmm/listboxrow.h> +#include <gtkmm/recentinfo.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/searchbar.h> +#include <gtkmm/searchentry.h> +#include <gtkmm/viewport.h> + +#include "inkscape.h" +#include "ui/dialog/align-and-distribute.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +// Enables using switch case +enum class TypeOfVariant +{ + NONE, + UNKNOWN, + BOOL, + INT, + DOUBLE, + STRING, + TUPLE_DD +}; + +enum class CPMode +{ + SEARCH, + INPUT, // Input arguments + SHELL, + HISTORY +}; + +enum class HistoryType +{ + LPE, + ACTION, + OPEN_FILE, + IMPORT_FILE, +}; + +struct History +{ + HistoryType history_type; + std::string data; + + History(HistoryType ht, std::string &&data) + : history_type(ht) + , data(data) + {} +}; + +class CPHistoryXML +{ +public: + // constructors, asssignment, destructor + CPHistoryXML(); + ~CPHistoryXML(); + + // Handy wrappers for code clearity + void add_action(const std::string &full_action_name); + + void add_import(const std::string &uri); + void add_open(const std::string &uri); + + // Remember parameter for action + void add_action_parameter(const std::string &full_action_name, const std::string ¶m); + + std::optional<History> get_last_operation(); + + // To construct _CPHistory + std::vector<History> get_operation_history() const; + // To get parameter history when an action is selected, LIFO stack like so more recent first + std::vector<std::string> get_action_parameter_history(const std::string &full_action_name) const; + +private: + void save() const; + + void add_operation(const HistoryType history_type, const std::string &data); + + static std::optional<HistoryType> _get_operation_type(Inkscape::XML::Node *operation); + + const std::string _file_path; + + Inkscape::XML::Document *_xml_doc; + // handy for xml doc child + Inkscape::XML::Node *_operations; + Inkscape::XML::Node *_params; +}; + +class CommandPalette +{ +public: // API + CommandPalette(); + ~CommandPalette() = default; + + CommandPalette(CommandPalette const &) = delete; // no copy + CommandPalette &operator=(CommandPalette const &) = delete; // no assignment + + void open(); + void close(); + void toggle(); + + Gtk::Box *get_base_widget(); + +private: // Helpers + using ActionPtr = Glib::RefPtr<Gio::Action>; + using ActionPtrName = std::pair<ActionPtr, Glib::ustring>; + + /** + * Insert actions in _CPSuggestions + */ + void load_app_actions(); + void load_win_doc_actions(); + + void append_recent_file_operation(const Glib::ustring &path, bool is_suggestion, bool is_import = true); + bool generate_action_operation(const ActionPtrName &action_ptr_name, const bool is_suggestion); + +private: // Signal handlers + void on_search(); + + int on_filter_general(Gtk::ListBoxRow *child); + bool on_filter_full_action_name(Gtk::ListBoxRow *child); + bool on_filter_recent_file(Gtk::ListBoxRow *child, bool const is_import); + + bool on_key_press_cpfilter_escape(GdkEventKey *evt); + bool on_key_press_cpfilter_search_mode(GdkEventKey *evt); + bool on_key_press_cpfilter_input_mode(GdkEventKey *evt, const ActionPtrName &action_ptr_name); + bool on_key_press_cpfilter_history_mode(GdkEventKey *evt); + + /** + * when search bar is empty + */ + void hide_suggestions(); + + /** + * when search bar isn't empty + */ + void show_suggestions(); + + void on_row_activated(Gtk::ListBoxRow *activated_row); + void on_history_selection_changed(Gtk::ListBoxRow *lb); + + bool operate_recent_file(Glib::ustring const &uri, bool const import); + + void on_action_fullname_clicked(const Glib::ustring &action_fullname); + + /** + * Implements text matching logic + */ + static bool fuzzy_search(const Glib::ustring &subject, const Glib::ustring &search); + static bool normal_search(const Glib::ustring &subject, const Glib::ustring &search); + static bool fuzzy_tolerance_search(const Glib::ustring &subject, const Glib::ustring &search); + static int fuzzy_points(const Glib::ustring &subject, const Glib::ustring &search); + static int fuzzy_tolerance_points(const Glib::ustring &subject, const Glib::ustring &search); + static int fuzzy_points_compare(int fuzzy_points_count_1, int fuzzy_points_count_2, int text_len_1, int text_len_2); + int on_sort(Gtk::ListBoxRow *row1, Gtk::ListBoxRow *row2); + void set_mode(CPMode mode); + + /** + * Color addition in searched character + */ + void add_color(Gtk::Label *label, const Glib::ustring &search, const Glib::ustring &subject, bool tooltip=false); + void remove_color(Gtk::Label *label, const Glib::ustring &subject, bool tooltip=false); + static void add_color_description(Gtk::Label *label, const Glib::ustring &search); + + /** + * Executes Action + */ + bool ask_action_parameter(const ActionPtrName &action); + static ActionPtrName get_action_ptr_name(const Glib::ustring &full_action_name); + bool execute_action(const ActionPtrName &action, const Glib::ustring &value); + + static TypeOfVariant get_action_variant_type(const ActionPtr &action_ptr); + + static std::pair<Gtk::Label *, Gtk::Label *> get_name_desc(Gtk::ListBoxRow *child); + Gtk::Button *get_full_action_name(Gtk::ListBoxRow *child); + +private: // variables + // Widgets + Glib::RefPtr<Gtk::Builder> _builder; + + Gtk::Box *_CPBase; + Gtk::Box *_CPHeader; + Gtk::Box *_CPListBase; + + Gtk::SearchBar *_CPSearchBar; + Gtk::SearchEntry *_CPFilter; + + Gtk::ListBox *_CPSuggestions; + Gtk::ListBox *_CPHistory; + + Gtk::ScrolledWindow *_CPSuggestionsScroll; + Gtk::ScrolledWindow *_CPHistoryScroll; + + // Data + const int _max_height_requestable = 360; + Glib::ustring _search_text; + + // States + bool _is_open = false; + bool _win_doc_actions_loaded = false; + + // History + CPHistoryXML _history_xml; + /** + * Remember the mode we are in helps in unnecessary signal disconnection and reconnection + * Used by set_mode() + */ + CPMode _mode = CPMode::SHELL; + // Default value other than SEARCH required + // set_mode() switches between mode hence checks if it already in the target mode. + // Constructed value is sometimes SEARCH being the first Item for now + // set_mode() never attaches the on search listener then + // This initialising value can be any thing other than the initial required mode + // Example currently it's open in search mode + + /** + * Stores the search connection to deactivate when not needed + */ + sigc::connection _cpfilter_search_connection; + /** + * Stores the key_press connection to deactivate when not needed + */ + sigc::connection _cpfilter_key_press_connection; +}; + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +#endif // INKSCAPE_DIALOG_COMMAND_PALETTE_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |