Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
completion-popup.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2
3#include <cassert>
4#include <gtkmm/entrycompletion.h>
5#include <gtkmm/eventcontrollerkey.h>
6#include <gtkmm/eventcontrollerfocus.h>
7#include <gtkmm/menubutton.h>
8#include <gtkmm/entry.h>
9#include <gtkmm/liststore.h>
10
11#include "completion-popup.h"
12#include "ui/builder-utils.h"
13#include <locale>
14#include <codecvt>
15
16namespace Inkscape::UI::Widget {
17
24
26 _builder(create_builder("completion-box.glade")),
27 _search(get_widget<Gtk::Entry>(_builder, "search")),
28 _button(get_widget<Gtk::MenuButton>(_builder, "menu-btn")),
29 _popover_menu{Gtk::PositionType::BOTTOM},
30 _completion(get_object<Gtk::EntryCompletion>(_builder, "completion"))
31{
32 auto const key = Gtk::EventControllerKey::create();
33 key->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
34 key->signal_key_pressed().connect(sigc::mem_fun(*this, &CompletionPopup::onPopoverKeyPressed), true);
35 _popover_menu.add_controller(key);
36 _button.set_popover(_popover_menu);
37
38 _list = std::dynamic_pointer_cast<Gtk::ListStore>(_builder->get_object("list"));
39 assert(_list);
40
41 append(get_widget<Gtk::Box>(_builder, "main-box"));
42
43 _completion->set_match_func([=](const Glib::ustring& text, const Gtk::TreeModel::const_iterator& it){
44 Glib::ustring str;
45 it->get_value(ColSearch, str);
46 if (str.empty()) {
47 return false;
48 }
49 return str.normalize().lowercase().find(text.normalize().lowercase()) != Glib::ustring::npos;
50 });
51
52 _completion->signal_match_selected().connect([this](const Gtk::TreeModel::iterator& it){
53 int id;
54 it->get_value(ColID, id);
55 _match_selected.emit(id);
56 clear();
57 return true;
58 }, false);
59
60 auto focus = Gtk::EventControllerFocus::create();
61 focus->property_contains_focus().signal_changed().connect([this, &focus = *focus] {
62 if (focus.contains_focus()) {
63 _on_focus.emit();
64 }
65 });
66 _search.add_controller(focus);
67
68 _button.property_active().signal_changed().connect([this] {
69 if (!_button.get_active()) {
70 return;
71 }
72 _button_press.emit();
73 clear();
74 _menu_search.clear();
76 });
77}
78
80
81bool CompletionPopup::onPopoverKeyPressed(unsigned keyval, unsigned /*keycode*/, Gdk::ModifierType /*state*/) {
82 if (!_button.get_active()) {
83 return false;
84 }
85 switch (keyval) {
86 case GDK_KEY_Left:
87 case GDK_KEY_KP_Left:
88 case GDK_KEY_Up:
89 case GDK_KEY_KP_Up:
90 case GDK_KEY_Right:
91 case GDK_KEY_KP_Right:
92 case GDK_KEY_Down:
93 case GDK_KEY_KP_Down:
94 _menu_search.clear();
96 return false;
97 break;
98 case GDK_KEY_BackSpace:
99 if (int len = _menu_search.size()) {
101 _menu_search = _menu_search.erase(len - 1);
103 return true;
104 }
105 break;
106 default:
107 break;
108 }
109 int const ucode = gdk_keyval_to_unicode(gdk_keyval_to_lower(keyval));
110 if (!std::isalpha(ucode) && keyval != GDK_KEY_minus) {
111 return false;
112 }
113 std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv1;
114 std::string charutf = conv1.to_bytes(ucode);
115 _menu_search += charutf;
117 return true;
118}
119
123
124void CompletionPopup::add_to_completion_list(int id, Glib::ustring name, Glib::ustring icon_name, Glib::ustring search_text) {
125 auto row = *_list->append();
126 row.set_value(ColID, id);
127 row.set_value(ColName, name);
128 row.set_value(ColIcon, icon_name);
129 row.set_value(ColSearch, search_text.empty() ? name : search_text);
130}
131
135
137 return _search;
138}
139
140sigc::signal<void (int)>& CompletionPopup::on_match_selected() {
141 return _match_selected;
142}
143
144sigc::signal<void ()>& CompletionPopup::on_button_press() {
145 return _button_press;
146}
147
148sigc::signal<bool ()>& CompletionPopup::on_focus() {
149 return _on_focus;
150}
151
154 _search.set_text({});
155}
156
157} // namespace Inkscape::UI::Widget
158
159/*
160 Local Variables:
161 mode:c++
162 c-file-style:"stroustrup"
163 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
164 indent-tabs-mode:nil
165 fill-column:99
166 End:
167*/
168// vim:filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99:
Gtk builder utilities.
void clear()
Clear search box without triggering completion popup menu.
Glib::RefPtr< Gtk::ListStore > _list
Glib::RefPtr< Gtk::Builder > _builder
void add_to_completion_list(int id, Glib::ustring name, Glib::ustring icon_name, Glib::ustring search_text={})
sigc::signal< void(int)> & on_match_selected()
Glib::RefPtr< Gtk::EntryCompletion > _completion
sigc::signal< void()> & on_button_press()
sigc::signal< void(int)> _match_selected
bool onPopoverKeyPressed(unsigned keyval, unsigned keycode, Gdk::ModifierType state)
Helperclass for Gtk::Entry widgets.
Definition entry.h:24
A replacement for GTK3ʼs Gtk::Menu, as removed in GTK4.
bool activate(Glib::ustring const &search)
Find and active from string.
void unset_items_focus_hover(Gtk::Widget *except_active)
Definition desktop.h:50
Custom widgets.
Definition desktop.h:126
Glib::RefPtr< Ob > get_object(Glib::RefPtr< Gtk::Builder > const &builder, char const *id)
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
static void append(std::vector< T > &target, std::vector< T > &&source)
static cairo_user_data_key_t key
auto len
Definition safe-printf.h:21
Glib::ustring name
Definition toolbars.cpp:55