1 #include "parameter_set_editor.hpp"
2 #include "spin_button_dim.hpp"
3 #include "common/common.hpp"
4
5 namespace horizon {
6
header_fun(Gtk::ListBoxRow * row,Gtk::ListBoxRow * before)7 static void header_fun(Gtk::ListBoxRow *row, Gtk::ListBoxRow *before)
8 {
9 if (before && !row->get_header()) {
10 auto ret = Gtk::manage(new Gtk::Separator);
11 row->set_header(*ret);
12 }
13 }
14
15 class ParameterEditor : public Gtk::Box {
16 public:
ParameterEditor(ParameterID id,ParameterSetEditor * pse)17 ParameterEditor(ParameterID id, ParameterSetEditor *pse)
18 : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 8), parameter_id(id), parent(pse)
19 {
20 auto la = Gtk::manage(new Gtk::Label(parameter_id_to_name(id)));
21 la->get_style_context()->add_class("dim-label");
22 la->set_xalign(1);
23 parent->sg_label->add_widget(*la);
24 pack_start(*la, false, false, 0);
25
26 sp = Gtk::manage(new SpinButtonDim());
27 sp->set_range(-1e9, 1e9);
28 sp->set_value(parent->parameter_set->at(id));
29 sp->signal_value_changed().connect([this] {
30 (*parent->parameter_set)[parameter_id] = sp->get_value_as_int();
31 parent->s_signal_changed.emit();
32 });
33 sp->signal_key_press_event().connect([this](GdkEventKey *ev) {
34 if (ev->keyval == GDK_KEY_Return && (ev->state & GDK_SHIFT_MASK)) {
35 sp->activate();
36 parent->s_signal_apply_all.emit(parameter_id);
37 parent->s_signal_changed.emit();
38 return true;
39 }
40 return false;
41 });
42 sp->signal_activate().connect([this] {
43 auto widgets = parent->listbox->get_children();
44 auto my_row = sp->get_ancestor(GTK_TYPE_LIST_BOX_ROW);
45 auto next = std::find(widgets.begin(), widgets.end(), my_row) + 1;
46 if (next < widgets.end()) {
47 auto e = dynamic_cast<ParameterEditor *>(dynamic_cast<Gtk::ListBoxRow *>(*next)->get_child());
48 e->sp->grab_focus();
49 }
50 else {
51 parent->s_signal_activate_last.emit();
52 }
53 });
54 pack_start(*sp, true, true, 0);
55
56 auto delete_button = Gtk::manage(new Gtk::Button());
57 delete_button->set_image_from_icon_name("list-remove-symbolic", Gtk::ICON_SIZE_BUTTON);
58 pack_start(*delete_button, false, false, 0);
59 delete_button->signal_clicked().connect([this] {
60 parent->s_signal_remove_extra_widget.emit(parameter_id);
61 parent->s_signal_apply_all_toggled.emit(parameter_id, false);
62 parent->parameter_set->erase(parameter_id);
63 parent->s_signal_changed.emit();
64 delete this->get_parent();
65 });
66
67 for (auto extra_widget : parent->s_signal_create_extra_widget.emit(id)) {
68 if (auto ch = dynamic_cast<Changeable *>(extra_widget)) {
69 ch->signal_changed().connect([this] { parent->s_signal_changed.emit(); });
70 }
71 pack_start(*extra_widget, false, false, 0);
72 }
73 if (auto bu = parent->create_apply_all_button(id)) {
74 apply_all_toggle_button = dynamic_cast<Gtk::ToggleButton *>(bu);
75 pack_start(*bu, false, false, 0);
76 }
77
78 set_margin_start(4);
79 set_margin_end(4);
80 set_margin_top(4);
81 set_margin_bottom(4);
82 show_all();
83 }
84 SpinButtonDim *sp = nullptr;
85 Gtk::ToggleButton *apply_all_toggle_button = nullptr;
86
87
88 const ParameterID parameter_id;
89
90 private:
91 ParameterSetEditor *parent;
92 };
93
ParameterSetEditor(ParameterSet * ps,bool populate_init)94 ParameterSetEditor::ParameterSetEditor(ParameterSet *ps, bool populate_init)
95 : Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0), parameter_set(ps)
96 {
97 add_button = Gtk::manage(new Gtk::MenuButton());
98 add_button->set_label("Add parameter");
99 add_button->set_halign(Gtk::ALIGN_START);
100 add_button->set_margin_bottom(10);
101 add_button->set_margin_top(10);
102 add_button->set_margin_start(10);
103 add_button->set_margin_end(10);
104 add_button->show();
105 pack_start(*add_button, false, false, 0);
106
107
108 add_button->set_menu(menu);
109 menu.signal_show().connect(sigc::mem_fun(*this, &ParameterSetEditor::update_menu));
110 for (int i = 1; i < (static_cast<int>(ParameterID::N_PARAMETERS)); i++) {
111 auto id = static_cast<ParameterID>(i);
112 auto item = Gtk::manage(new Gtk::MenuItem(parameter_id_to_name(id)));
113 item->signal_activate().connect([this, id] {
114 (*parameter_set)[id] = 0.5_mm;
115 auto pe = Gtk::manage(new ParameterEditor(id, this));
116 listbox->add(*pe);
117 s_signal_changed.emit();
118 });
119 menu.append(*item);
120 menu_items.emplace(id, *item);
121 }
122
123 auto sc = Gtk::manage(new Gtk::ScrolledWindow());
124 sc->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
125 listbox = Gtk::manage(new Gtk::ListBox());
126 listbox->set_selection_mode(Gtk::SELECTION_NONE);
127 listbox->set_header_func(&header_fun);
128 sc->add(*listbox);
129
130 sg_label = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
131 if (populate_init)
132 populate();
133
134 sc->show_all();
135 {
136 auto sep = Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL));
137 sep->show();
138 pack_start(*sep, false, false, 0);
139 }
140 pack_start(*sc, true, true, 0);
141 }
142
set_has_apply_all(const std::string & tooltip_text)143 void ParameterSetEditor::set_has_apply_all(const std::string &tooltip_text)
144 {
145 apply_all_tooltip_text.emplace(tooltip_text);
146 }
147
set_has_apply_all_toggle(const std::string & tooltip_text)148 void ParameterSetEditor::set_has_apply_all_toggle(const std::string &tooltip_text)
149 {
150 apply_all_tooltip_text.emplace(tooltip_text);
151 apply_all_toggle = true;
152 }
153
154
set_button_margin_left(int margin)155 void ParameterSetEditor::set_button_margin_left(int margin)
156 {
157 add_button->set_margin_start(margin);
158 }
159
populate()160 void ParameterSetEditor::populate()
161 {
162 for (auto &it : *parameter_set) {
163 auto pe = Gtk::manage(new ParameterEditor(it.first, this));
164 listbox->add(*pe);
165 }
166 listbox->show_all();
167 }
168
focus_first()169 void ParameterSetEditor::focus_first()
170 {
171 auto widgets = listbox->get_children();
172 if (widgets.size()) {
173 auto w = dynamic_cast<ParameterEditor *>(dynamic_cast<Gtk::ListBoxRow *>(widgets.front())->get_child());
174 w->sp->grab_focus();
175 }
176 }
177
add_or_set_parameter(ParameterID param,int64_t value)178 void ParameterSetEditor::add_or_set_parameter(ParameterID param, int64_t value)
179 {
180 if (parameter_set->count(param)) {
181 auto rows = listbox->get_children();
182 for (auto row : rows) {
183 auto w = dynamic_cast<ParameterEditor *>(dynamic_cast<Gtk::ListBoxRow *>(row)->get_child());
184 if (w->parameter_id == param)
185 w->sp->set_value(value);
186 }
187 }
188 else {
189 (*parameter_set)[param] = value;
190 auto pe = Gtk::manage(new ParameterEditor(param, this));
191 listbox->add(*pe);
192 }
193 s_signal_changed.emit();
194 }
195
update_menu()196 void ParameterSetEditor::update_menu()
197 {
198 for (int i = 1; i < (static_cast<int>(ParameterID::N_PARAMETERS)); i++) {
199 auto id = static_cast<ParameterID>(i);
200 menu_items.at(id).set_visible(parameter_set->count(id) == 0);
201 }
202 }
203
create_apply_all_button(ParameterID id)204 Gtk::Widget *ParameterSetEditor::create_apply_all_button(ParameterID id)
205 {
206 if (!apply_all_tooltip_text)
207 return nullptr;
208 if (apply_all_toggle) {
209 auto w = Gtk::manage(new Gtk::ToggleButton("All"));
210 w->set_tooltip_text(apply_all_tooltip_text.value());
211 w->signal_clicked().connect([this, id, w] { s_signal_apply_all_toggled.emit(id, w->get_active()); });
212 return w;
213 }
214 else {
215 auto w = Gtk::manage(new Gtk::Button());
216 w->set_image_from_icon_name("object-select-symbolic", Gtk::ICON_SIZE_BUTTON);
217 w->set_tooltip_text(apply_all_tooltip_text.value());
218 w->signal_clicked().connect([this, id] { s_signal_apply_all.emit(id); });
219 return w;
220 }
221 }
222
set_apply_all(std::set<ParameterID> params)223 void ParameterSetEditor::set_apply_all(std::set<ParameterID> params)
224 {
225 auto rows = listbox->get_children();
226 for (auto row : rows) {
227 auto w = dynamic_cast<ParameterEditor *>(dynamic_cast<Gtk::ListBoxRow *>(row)->get_child());
228 if (w->apply_all_toggle_button)
229 w->apply_all_toggle_button->set_active(params.count(w->parameter_id));
230 }
231 }
232
233 } // namespace horizon
234