Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/openxr/editor/openxr_interaction_profile_editor.cpp
10278 views
1
/**************************************************************************/
2
/* openxr_interaction_profile_editor.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "openxr_interaction_profile_editor.h"
32
#include "../openxr_api.h"
33
#include "editor/editor_string_names.h"
34
35
///////////////////////////////////////////////////////////////////////////
36
// Interaction profile editor base
37
38
void OpenXRInteractionProfileEditorBase::_bind_methods() {
39
ClassDB::bind_method(D_METHOD("setup", "action_map", "interaction_profile"), &OpenXRInteractionProfileEditorBase::setup);
40
41
ClassDB::bind_method(D_METHOD("_add_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_add_binding);
42
ClassDB::bind_method(D_METHOD("_remove_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_remove_binding);
43
}
44
45
void OpenXRInteractionProfileEditorBase::_notification(int p_what) {
46
switch (p_what) {
47
case NOTIFICATION_ENTER_TREE: {
48
_update_interaction_profile();
49
} break;
50
51
case NOTIFICATION_THEME_CHANGED: {
52
_theme_changed();
53
} break;
54
}
55
}
56
57
void OpenXRInteractionProfileEditorBase::_do_update_interaction_profile() {
58
if (!is_dirty) {
59
is_dirty = true;
60
callable_mp(this, &OpenXRInteractionProfileEditorBase::_update_interaction_profile).call_deferred();
61
}
62
}
63
64
void OpenXRInteractionProfileEditorBase::_add_binding(const String p_action, const String p_path) {
65
ERR_FAIL_COND(action_map.is_null());
66
ERR_FAIL_COND(interaction_profile.is_null());
67
68
Ref<OpenXRAction> action = action_map->get_action(p_action);
69
ERR_FAIL_COND(action.is_null());
70
71
Ref<OpenXRIPBinding> binding = interaction_profile->find_binding(action, p_path);
72
if (binding.is_null()) {
73
// create a new binding
74
binding.instantiate();
75
binding->set_action(action);
76
binding->set_binding_path(p_path);
77
78
// add it to our interaction profile
79
interaction_profile->add_binding(binding);
80
interaction_profile->set_edited(true);
81
82
binding->set_edited(true);
83
}
84
85
// Update our toplevel paths
86
action->set_toplevel_paths(action_map->get_top_level_paths(action));
87
88
_do_update_interaction_profile();
89
}
90
91
void OpenXRInteractionProfileEditorBase::_remove_binding(const String p_action, const String p_path) {
92
ERR_FAIL_COND(action_map.is_null());
93
ERR_FAIL_COND(interaction_profile.is_null());
94
95
Ref<OpenXRAction> action = action_map->get_action(p_action);
96
ERR_FAIL_COND(action.is_null());
97
98
Ref<OpenXRIPBinding> binding = interaction_profile->find_binding(action, p_path);
99
if (binding.is_valid()) {
100
interaction_profile->remove_binding(binding);
101
interaction_profile->set_edited(true);
102
103
// Update our toplevel paths
104
action->set_toplevel_paths(action_map->get_top_level_paths(action));
105
106
_do_update_interaction_profile();
107
}
108
}
109
110
void OpenXRInteractionProfileEditorBase::_update_interaction_profile() {
111
if (!is_dirty) {
112
// no need to update
113
return;
114
}
115
116
// Nothing to do here for now..
117
118
// and we've updated it...
119
is_dirty = false;
120
}
121
122
void OpenXRInteractionProfileEditorBase::_theme_changed() {
123
if (binding_modifiers_btn) {
124
binding_modifiers_btn->set_button_icon(get_theme_icon(SNAME("Modifiers"), EditorStringName(EditorIcons)));
125
}
126
}
127
128
void OpenXRInteractionProfileEditorBase::remove_all_for_action_set(Ref<OpenXRActionSet> p_action_set) {
129
// Note, don't need to remove bindings themselves as remove_all_for_action will be called for each before this is called.
130
131
// TODO update binding modifiers
132
}
133
134
void OpenXRInteractionProfileEditorBase::remove_all_for_action(Ref<OpenXRAction> p_action) {
135
Vector<Ref<OpenXRIPBinding>> bindings = interaction_profile->get_bindings_for_action(p_action);
136
if (bindings.size() > 0) {
137
String action_name = p_action->get_name_with_set();
138
139
// For our undo/redo we process all paths
140
undo_redo->create_action(TTR("Remove action from interaction profile"));
141
for (const Ref<OpenXRIPBinding> &binding : bindings) {
142
undo_redo->add_do_method(this, "_remove_binding", action_name, binding->get_binding_path());
143
undo_redo->add_undo_method(this, "_add_binding", action_name, binding->get_binding_path());
144
}
145
undo_redo->commit_action(false);
146
147
// But remove them all in one go so we're more efficient in updating our UI.
148
for (const Ref<OpenXRIPBinding> &binding : bindings) {
149
interaction_profile->remove_binding(binding);
150
}
151
interaction_profile->set_edited(true);
152
153
// Update our toplevel paths
154
p_action->set_toplevel_paths(action_map->get_top_level_paths(p_action));
155
156
_do_update_interaction_profile();
157
}
158
}
159
void OpenXRInteractionProfileEditorBase::_on_open_binding_modifiers() {
160
binding_modifiers_dialog->popup_centered(Size2i(500, 400));
161
}
162
163
OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase() {
164
undo_redo = EditorUndoRedoManager::get_singleton();
165
166
set_h_size_flags(SIZE_EXPAND_FILL);
167
set_v_size_flags(SIZE_EXPAND_FILL);
168
169
interaction_profile_sc = memnew(ScrollContainer);
170
interaction_profile_sc->set_h_size_flags(SIZE_EXPAND_FILL);
171
interaction_profile_sc->set_v_size_flags(SIZE_EXPAND_FILL);
172
add_child(interaction_profile_sc);
173
174
binding_modifiers_dialog = memnew(OpenXRBindingModifiersDialog);
175
add_child(binding_modifiers_dialog);
176
177
toolbar_vb = memnew(VBoxContainer);
178
toolbar_vb->set_v_size_flags(SIZE_EXPAND_FILL);
179
add_child(toolbar_vb);
180
181
binding_modifiers_btn = memnew(Button);
182
binding_modifiers_btn->set_tooltip_text(TTR("Edit binding modifiers"));
183
binding_modifiers_btn->connect("pressed", callable_mp(this, &OpenXRInteractionProfileEditorBase::_on_open_binding_modifiers));
184
// TODO show visual difference if there are binding modifiers for this interaction profile
185
toolbar_vb->add_child(binding_modifiers_btn);
186
}
187
188
void OpenXRInteractionProfileEditorBase::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
189
ERR_FAIL_NULL(binding_modifiers_dialog);
190
binding_modifiers_dialog->setup(p_action_map, p_interaction_profile);
191
192
action_map = p_action_map;
193
interaction_profile = p_interaction_profile;
194
String profile_path = interaction_profile->get_interaction_profile_path();
195
String profile_name = profile_path;
196
197
profile_def = OpenXRInteractionProfileMetadata::get_singleton()->get_profile(profile_path);
198
if (profile_def != nullptr) {
199
profile_name = profile_def->display_name;
200
201
if (!profile_def->openxr_extension_name.is_empty()) {
202
profile_name += "*";
203
204
tooltip = vformat(TTR("Note: This interaction profile requires extension %s support."), profile_def->openxr_extension_name);
205
}
206
}
207
208
set_name(profile_name);
209
210
// Make sure it is updated when it enters the tree...
211
is_dirty = true;
212
}
213
214
///////////////////////////////////////////////////////////////////////////
215
// Default interaction profile editor
216
217
void OpenXRInteractionProfileEditor::select_action_for(const String p_io_path) {
218
selecting_for_io_path = p_io_path;
219
select_action_dialog->open();
220
}
221
222
void OpenXRInteractionProfileEditor::_on_action_selected(const String p_action) {
223
undo_redo->create_action(TTR("Add binding"));
224
undo_redo->add_do_method(this, "_add_binding", p_action, selecting_for_io_path);
225
undo_redo->add_undo_method(this, "_remove_binding", p_action, selecting_for_io_path);
226
undo_redo->commit_action(true);
227
228
selecting_for_io_path = "";
229
}
230
231
void OpenXRInteractionProfileEditor::_on_remove_pressed(const String p_action, const String p_for_io_path) {
232
undo_redo->create_action(TTR("Remove binding"));
233
undo_redo->add_do_method(this, "_remove_binding", p_action, p_for_io_path);
234
undo_redo->add_undo_method(this, "_add_binding", p_action, p_for_io_path);
235
undo_redo->commit_action(true);
236
}
237
238
void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetadata::IOPath *p_io_path) {
239
HBoxContainer *path_hb = memnew(HBoxContainer);
240
path_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
241
p_container->add_child(path_hb);
242
243
Label *path_label = memnew(Label);
244
path_label->set_focus_mode(FOCUS_ACCESSIBILITY);
245
if (p_io_path->openxr_extension_name.is_empty()) {
246
path_label->set_text(p_io_path->display_name);
247
} else {
248
path_label->set_text(p_io_path->display_name + "*");
249
path_hb->set_tooltip_text(vformat(TTR("Note: This binding path requires extension %s support."), p_io_path->openxr_extension_name));
250
}
251
path_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
252
path_hb->add_child(path_label);
253
254
Label *type_label = memnew(Label);
255
type_label->set_focus_mode(FOCUS_ACCESSIBILITY);
256
switch (p_io_path->action_type) {
257
case OpenXRAction::OPENXR_ACTION_BOOL: {
258
type_label->set_text(TTR("Boolean"));
259
} break;
260
case OpenXRAction::OPENXR_ACTION_FLOAT: {
261
type_label->set_text(TTR("Float"));
262
} break;
263
case OpenXRAction::OPENXR_ACTION_VECTOR2: {
264
type_label->set_text(TTR("Vector2"));
265
} break;
266
case OpenXRAction::OPENXR_ACTION_POSE: {
267
type_label->set_text(TTR("Pose"));
268
} break;
269
case OpenXRAction::OPENXR_ACTION_HAPTIC: {
270
type_label->set_text(TTR("Haptic"));
271
} break;
272
default: {
273
type_label->set_text(TTR("Unknown"));
274
} break;
275
}
276
type_label->set_custom_minimum_size(Size2(50.0, 0.0));
277
path_hb->add_child(type_label);
278
279
Button *path_add = memnew(Button);
280
path_add->set_button_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
281
path_add->set_flat(true);
282
path_add->connect(SceneStringName(pressed), callable_mp(this, &OpenXRInteractionProfileEditor::select_action_for).bind(String(p_io_path->openxr_path)));
283
path_hb->add_child(path_add);
284
285
if (interaction_profile.is_valid()) {
286
String io_path = String(p_io_path->openxr_path);
287
Array bindings = interaction_profile->get_bindings();
288
for (Ref<OpenXRIPBinding> binding : bindings) {
289
if (binding->get_binding_path() == io_path) {
290
Ref<OpenXRAction> action = binding->get_action();
291
292
HBoxContainer *action_hb = memnew(HBoxContainer);
293
action_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
294
p_container->add_child(action_hb);
295
296
Control *indent_node = memnew(Control);
297
indent_node->set_custom_minimum_size(Size2(10.0, 0.0));
298
action_hb->add_child(indent_node);
299
300
Label *action_label = memnew(Label);
301
action_label->set_focus_mode(FOCUS_ACCESSIBILITY);
302
action_label->set_text(action->get_name_with_set() + ": " + action->get_localized_name());
303
action_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
304
action_hb->add_child(action_label);
305
306
OpenXRBindingModifiersDialog *action_binding_modifiers_dialog = memnew(OpenXRBindingModifiersDialog);
307
action_binding_modifiers_dialog->setup(action_map, interaction_profile, binding);
308
action_hb->add_child(action_binding_modifiers_dialog);
309
310
Button *action_binding_modifiers_btn = memnew(Button);
311
action_binding_modifiers_btn->set_flat(true);
312
action_binding_modifiers_btn->set_button_icon(get_theme_icon(SNAME("Modifiers"), EditorStringName(EditorIcons)));
313
action_binding_modifiers_btn->connect(SceneStringName(pressed), callable_mp((Window *)action_binding_modifiers_dialog, &Window::popup_centered).bind(Size2i(500, 400)));
314
action_binding_modifiers_btn->set_accessibility_name(TTRC("Modifiers"));
315
// TODO change style of button if there are binding modifiers
316
action_hb->add_child(action_binding_modifiers_btn);
317
318
Button *action_rem = memnew(Button);
319
action_rem->set_flat(true);
320
action_rem->set_button_icon(get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
321
action_rem->connect(SceneStringName(pressed), callable_mp((OpenXRInteractionProfileEditor *)this, &OpenXRInteractionProfileEditor::_on_remove_pressed).bind(action->get_name_with_set(), String(p_io_path->openxr_path)));
322
action_rem->set_accessibility_name(TTRC("Remove"));
323
action_hb->add_child(action_rem);
324
}
325
}
326
}
327
}
328
329
void OpenXRInteractionProfileEditor::_update_interaction_profile() {
330
ERR_FAIL_NULL(profile_def);
331
332
if (!is_dirty) {
333
// no need to update
334
return;
335
}
336
337
PackedStringArray requested_extensions = OpenXRAPI::get_all_requested_extensions();
338
339
// out with the old...
340
while (interaction_profile_hb->get_child_count() > 0) {
341
memdelete(interaction_profile_hb->get_child(0));
342
}
343
344
// in with the new...
345
346
// Determine toplevel paths
347
Vector<String> top_level_paths;
348
for (int i = 0; i < profile_def->io_paths.size(); i++) {
349
const OpenXRInteractionProfileMetadata::IOPath *io_path = &profile_def->io_paths[i];
350
351
if (!top_level_paths.has(io_path->top_level_path)) {
352
top_level_paths.push_back(io_path->top_level_path);
353
}
354
}
355
356
for (int i = 0; i < top_level_paths.size(); i++) {
357
PanelContainer *panel = memnew(PanelContainer);
358
panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
359
interaction_profile_hb->add_child(panel);
360
panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainer")));
361
362
VBoxContainer *container = memnew(VBoxContainer);
363
panel->add_child(container);
364
365
Label *label = memnew(Label);
366
label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
367
label->set_text(OpenXRInteractionProfileMetadata::get_singleton()->get_top_level_name(top_level_paths[i]));
368
container->add_child(label);
369
370
for (int j = 0; j < profile_def->io_paths.size(); j++) {
371
const OpenXRInteractionProfileMetadata::IOPath *io_path = &profile_def->io_paths[j];
372
if (io_path->top_level_path == top_level_paths[i] && (io_path->openxr_extension_name.is_empty() || requested_extensions.has(io_path->openxr_extension_name))) {
373
_add_io_path(container, io_path);
374
}
375
}
376
}
377
378
OpenXRInteractionProfileEditorBase::_update_interaction_profile();
379
}
380
381
void OpenXRInteractionProfileEditor::_theme_changed() {
382
OpenXRInteractionProfileEditorBase::_theme_changed();
383
384
interaction_profile_sc->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
385
386
for (int i = 0; i < interaction_profile_hb->get_child_count(); i++) {
387
Control *panel = Object::cast_to<Control>(interaction_profile_hb->get_child(i));
388
if (panel) {
389
panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainer")));
390
}
391
}
392
}
393
394
OpenXRInteractionProfileEditor::OpenXRInteractionProfileEditor() {
395
interaction_profile_hb = memnew(HBoxContainer);
396
interaction_profile_sc->add_child(interaction_profile_hb);
397
}
398
399
void OpenXRInteractionProfileEditor::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
400
OpenXRInteractionProfileEditorBase::setup(p_action_map, p_interaction_profile);
401
402
select_action_dialog = memnew(OpenXRSelectActionDialog(p_action_map));
403
select_action_dialog->connect("action_selected", callable_mp(this, &OpenXRInteractionProfileEditor::_on_action_selected));
404
add_child(select_action_dialog);
405
}
406
407