diff --git a/src/ui/widget/completion-popup.cpp b/src/ui/widget/completion-popup.cpp index e5891fd1f0ffa76739249cf6fe7345f4742e3d53..2126a04d4ae209aea3034ba8d2560d99b0e35523 100644 --- a/src/ui/widget/completion-popup.cpp +++ b/src/ui/widget/completion-popup.cpp @@ -8,6 +8,9 @@ #include "completion-popup.h" #include "ui/builder-utils.h" +#include "ui/controller.h" +#include +#include namespace Inkscape::UI::Widget { @@ -26,6 +29,7 @@ CompletionPopup::CompletionPopup() : _completion(get_object(_builder, "completion")) { _popover_menu.show_all_children(); + Controller::add_key<&CompletionPopup::onPopoverKeyPressed>(_popover_menu, *this, Gtk::PHASE_CAPTURE); _button.set_popover(_popover_menu); _list = Glib::RefPtr::cast_dynamic(_builder->get_object("list")); @@ -63,6 +67,8 @@ CompletionPopup::CompletionPopup() : } _button_press.emit(); clear(); + _menu_search.clear(); + _popover_menu.activate({}); }, false); _search.signal_stop_search().connect([this](){ @@ -74,6 +80,47 @@ CompletionPopup::CompletionPopup() : CompletionPopup::~CompletionPopup() = default; +bool CompletionPopup::onPopoverKeyPressed(GtkEventControllerKey const * /*controller*/, + unsigned keyval, unsigned /*keycode*/, + GdkModifierType state) { + if (!_button.get_active()) { + return false; + } + switch (keyval) { + case GDK_KEY_Left: + case GDK_KEY_KP_Left: + case GDK_KEY_Up: + case GDK_KEY_KP_Up: + case GDK_KEY_Right: + case GDK_KEY_KP_Right: + case GDK_KEY_Down: + case GDK_KEY_KP_Down: + _menu_search.clear(); + _popover_menu.activate({}); + return false; + break; + case GDK_KEY_BackSpace: + _popover_menu.unset_items_focus_hover(nullptr); + _popover_menu.activate({}); + _menu_search.clear(); + return true; + break; + default: + break; + } + int const ucode = gdk_keyval_to_unicode(gdk_keyval_to_lower(keyval)); + if (!std::isalpha(ucode)) { + return false; + } + std::wstring_convert, char32_t> conv1; + std::string charutf = conv1.to_bytes(ucode); + _menu_search += charutf; + if (!_popover_menu.activate(_menu_search)) { + _menu_search.clear(); + } + return true; +} + void CompletionPopup::clear_completion_list() { _list->clear(); } diff --git a/src/ui/widget/completion-popup.h b/src/ui/widget/completion-popup.h index 33aaa38f7941807fcc35aa213129788fcaf6c73a..dec54fcf894f08598cf302dc0af946ed0a009233 100644 --- a/src/ui/widget/completion-popup.h +++ b/src/ui/widget/completion-popup.h @@ -37,6 +37,8 @@ public: sigc::signal& on_focus(); private: + bool onPopoverKeyPressed(GtkEventControllerKey const *controller, + unsigned keyval, unsigned keycode, GdkModifierType state); Glib::RefPtr _builder; Glib::RefPtr _list; Gtk::SearchEntry& _search; @@ -46,7 +48,7 @@ private: sigc::signal _match_selected; sigc::signal _button_press; sigc::signal _on_focus; - + Glib::ustring _menu_search; void clear(); }; diff --git a/src/ui/widget/popover-menu.cpp b/src/ui/widget/popover-menu.cpp index 24e129bae29c467d633b82befe91c374f8ab7f60..9518da962bc3640e610f33250e7d3009018285aa 100644 --- a/src/ui/widget/popover-menu.cpp +++ b/src/ui/widget/popover-menu.cpp @@ -21,6 +21,7 @@ #include #include +#include "preferences.h" #include "ui/menuize.h" #include "ui/popup-menu.h" #include "ui/util.h" @@ -177,6 +178,53 @@ void PopoverMenu::set_scrolled_window_size() _scrolled_window.set_max_content_height(window.get_height() - 2 * padding); } +bool PopoverMenu::activate(Glib::ustring const &search) { + bool match = false; + if (_active_search && search.empty()) { + _active_search->set_markup(_top_separator); + return false; + } + for (auto const item : _items) { + for (auto const widg : UI::get_children(*item)) { + item->unset_state_flags(Gtk::STATE_FLAG_FOCUSED | Gtk::STATE_FLAG_PRELIGHT); + if (!_active_search) { + _active_search = dynamic_cast(widg); + if (_active_search) { + _top_separator = _active_search->get_text(); + } + } + if (!search.empty()) { + for (auto const mi : UI::get_children(*widg)) { + if (auto label = dynamic_cast(mi)) { + auto text_data = label->get_text(); + // if not matched and search == begining of label + if (!match && text_data.size() >= search.size() && + text_data.substr(0, search.size()).lowercase() == search) + { + match = true; + item->grab_focus(); + break; + } + } + } + } + } + } + if (_active_search) { + auto markup = _top_separator; + if (match) { + auto prefs = Inkscape::Preferences::get(); + Glib::ustring color = "#ddddddff"; + if (prefs->getBool("/theme/darkTheme", true)) { + color = "#444444ff"; + } + markup = Glib::ustring::compose("%1 %3 ", markup, color, search); + } + _active_search->set_markup(markup); + } + return match; +} + void PopoverMenu::unset_items_focus_hover(Gtk::Widget * const except_active) { for (auto const item : _items) { diff --git a/src/ui/widget/popover-menu.h b/src/ui/widget/popover-menu.h index a0cd7795f547debbba48e068d1a312652a1bb833..0f21ef26033f95cf27892d1374aba774dc5e7807 100644 --- a/src/ui/widget/popover-menu.h +++ b/src/ui/widget/popover-menu.h @@ -63,6 +63,9 @@ public: /// Append a horizontal separator. void append_separator(); + /// Find and active from string + bool activate(Glib::ustring const &search); + /// Replace Gtk::Menu::popup_at_pointer. If x or y /// offsets != 0, :pointing-to is set to {x,y,1,1} /// @a widget must be the parent passed to self constructor or a descendant. @@ -84,12 +87,14 @@ private: Gtk::ScrolledWindow &_scrolled_window; PopoverMenuGrid &_grid; std::vector _items; - + Gtk::Label *_active_search = nullptr; + Glib::ustring _top_separator; void check_child_invariants(); void set_scrolled_window_size(); // Let PopoverMenuItem call this without making it public API friend class PopoverMenuItem; + friend class CompletionPopup; void unset_items_focus_hover(Gtk::Widget *except_active); void remove_all(bool and_delete);