summaryrefslogtreecommitdiff
path: root/modules/openxr/editor
diff options
context:
space:
mode:
Diffstat (limited to 'modules/openxr/editor')
-rw-r--r--modules/openxr/editor/SCsub5
-rw-r--r--modules/openxr/editor/openxr_action_editor.cpp112
-rw-r--r--modules/openxr/editor/openxr_action_editor.h67
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.cpp370
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.h100
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.cpp218
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.h88
-rw-r--r--modules/openxr/editor/openxr_editor_plugin.cpp58
-rw-r--r--modules/openxr/editor/openxr_editor_plugin.h53
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.cpp272
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.h83
-rw-r--r--modules/openxr/editor/openxr_select_action_dialog.cpp135
-rw-r--r--modules/openxr/editor/openxr_select_action_dialog.h67
-rw-r--r--modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp125
-rw-r--r--modules/openxr/editor/openxr_select_interaction_profile_dialog.h66
15 files changed, 1819 insertions, 0 deletions
diff --git a/modules/openxr/editor/SCsub b/modules/openxr/editor/SCsub
new file mode 100644
index 0000000000..ccf67a80d0
--- /dev/null
+++ b/modules/openxr/editor/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/openxr/editor/openxr_action_editor.cpp b/modules/openxr/editor/openxr_action_editor.cpp
new file mode 100644
index 0000000000..e2a4f67f16
--- /dev/null
+++ b/modules/openxr/editor/openxr_action_editor.cpp
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* openxr_action_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "openxr_action_editor.h"
+
+void OpenXRActionEditor::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_editor")));
+}
+
+void OpenXRActionEditor::_theme_changed() {
+ rem_action->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+}
+
+void OpenXRActionEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ _theme_changed();
+ } break;
+ }
+}
+
+void OpenXRActionEditor::_on_action_name_changed(const String p_new_text) {
+ // TODO validate if entry is allowed
+
+ // If our localized name matches our action name, set this too
+ if (action->get_name() == action->get_localized_name()) {
+ action->set_localized_name(p_new_text);
+ action_localized_name->set_text(p_new_text);
+ }
+ action->set_name(p_new_text);
+}
+
+void OpenXRActionEditor::_on_action_localized_name_changed(const String p_new_text) {
+ action->set_localized_name(p_new_text);
+}
+
+void OpenXRActionEditor::_on_item_selected(int p_idx) {
+ ERR_FAIL_COND(p_idx < 0);
+ ERR_FAIL_COND(p_idx >= OpenXRAction::OPENXR_ACTION_MAX);
+
+ action->set_action_type(OpenXRAction::ActionType(p_idx));
+}
+
+void OpenXRActionEditor::_on_remove_action() {
+ emit_signal("remove", this);
+}
+
+OpenXRActionEditor::OpenXRActionEditor(Ref<OpenXRAction> p_action) {
+ action = p_action;
+
+ set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ action_name = memnew(LineEdit);
+ action_name->set_text(action->get_name());
+ action_name->set_custom_minimum_size(Size2(150.0, 0.0));
+ action_name->connect("text_changed", callable_mp(this, &OpenXRActionEditor::_on_action_name_changed));
+ add_child(action_name);
+
+ action_localized_name = memnew(LineEdit);
+ action_localized_name->set_text(action->get_localized_name());
+ action_localized_name->set_custom_minimum_size(Size2(150.0, 0.0));
+ action_localized_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ action_localized_name->connect("text_changed", callable_mp(this, &OpenXRActionEditor::_on_action_localized_name_changed));
+ add_child(action_localized_name);
+
+ action_type = memnew(OptionButton);
+ action_type->add_item("Bool", OpenXRAction::OPENXR_ACTION_BOOL);
+ action_type->add_item("Float", OpenXRAction::OPENXR_ACTION_FLOAT);
+ action_type->add_item("Vector2", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ action_type->add_item("Pose", OpenXRAction::OPENXR_ACTION_POSE);
+ action_type->add_item("Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ action_type->select(int(action->get_action_type()));
+ action_type->set_custom_minimum_size(Size2(100.0, 0.0));
+ action_type->connect("item_selected", callable_mp(this, &OpenXRActionEditor::_on_item_selected));
+ add_child(action_type);
+
+ // maybe add dropdown to edit our toplevel paths, or do we deduce them from our suggested bindings?
+
+ rem_action = memnew(Button);
+ rem_action->set_tooltip(TTR("Remove action"));
+ rem_action->connect("pressed", callable_mp(this, &OpenXRActionEditor::_on_remove_action));
+ rem_action->set_flat(true);
+ add_child(rem_action);
+}
diff --git a/modules/openxr/editor/openxr_action_editor.h b/modules/openxr/editor/openxr_action_editor.h
new file mode 100644
index 0000000000..6e1b7ab779
--- /dev/null
+++ b/modules/openxr/editor/openxr_action_editor.h
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* openxr_action_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OPENXR_ACTION_EDITOR_H
+#define OPENXR_ACTION_EDITOR_H
+
+#include "../action_map/openxr_action.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/text_edit.h"
+
+class OpenXRActionEditor : public HBoxContainer {
+ GDCLASS(OpenXRActionEditor, HBoxContainer);
+
+private:
+ Ref<OpenXRAction> action;
+
+ LineEdit *action_name = nullptr;
+ LineEdit *action_localized_name = nullptr;
+ OptionButton *action_type = nullptr;
+ Button *rem_action = nullptr;
+
+ void _theme_changed();
+ void _on_action_name_changed(const String p_new_text);
+ void _on_action_localized_name_changed(const String p_new_text);
+ void _on_item_selected(int p_idx);
+ void _on_remove_action();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ Ref<OpenXRAction> get_action() { return action; };
+ OpenXRActionEditor(Ref<OpenXRAction> p_action);
+};
+
+#endif // !OPENXR_ACTION_EDITOR_H
diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp
new file mode 100644
index 0000000000..6e9a2e1b61
--- /dev/null
+++ b/modules/openxr/editor/openxr_action_map_editor.cpp
@@ -0,0 +1,370 @@
+/*************************************************************************/
+/* openxr_action_map_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "openxr_action_map_editor.h"
+
+#include "core/config/project_settings.h"
+#include "editor/editor_file_dialog.h"
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
+
+// TODO implement redo/undo system
+
+void OpenXRActionMapEditor::_bind_methods() {
+ ClassDB::bind_method("_add_action_set_editor", &OpenXRActionMapEditor::_add_action_set_editor);
+ ClassDB::bind_method("_update_action_sets", &OpenXRActionMapEditor::_update_action_sets);
+
+ ClassDB::bind_method("_add_interaction_profile_editor", &OpenXRActionMapEditor::_add_interaction_profile_editor);
+ ClassDB::bind_method("_update_interaction_profiles", &OpenXRActionMapEditor::_update_interaction_profiles);
+
+ ClassDB::bind_method(D_METHOD("_add_action_set", "name"), &OpenXRActionMapEditor::_add_action_set);
+ ClassDB::bind_method(D_METHOD("_set_focus_on_action_set", "action_set"), &OpenXRActionMapEditor::_set_focus_on_action_set);
+ ClassDB::bind_method(D_METHOD("_remove_action_set", "name"), &OpenXRActionMapEditor::_remove_action_set);
+}
+
+void OpenXRActionMapEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ for (int i = 0; i < tabs->get_child_count(); i++) {
+ Control *tab = static_cast<Control *>(tabs->get_child(i));
+ if (tab) {
+ tab->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ }
+ }
+ } break;
+
+ case NOTIFICATION_READY: {
+ _update_action_sets();
+ _update_interaction_profiles();
+ } break;
+ }
+}
+
+OpenXRActionSetEditor *OpenXRActionMapEditor::_add_action_set_editor(Ref<OpenXRActionSet> p_action_set) {
+ ERR_FAIL_COND_V(p_action_set.is_null(), nullptr);
+
+ OpenXRActionSetEditor *action_set_editor = memnew(OpenXRActionSetEditor(action_map, p_action_set));
+ action_set_editor->connect("remove", callable_mp(this, &OpenXRActionMapEditor::_on_remove_action_set));
+ action_set_editor->connect("action_removed", callable_mp(this, &OpenXRActionMapEditor::_on_action_removed));
+ actionsets_vb->add_child(action_set_editor);
+
+ return action_set_editor;
+}
+
+void OpenXRActionMapEditor::_update_action_sets() {
+ // out with the old...
+ while (actionsets_vb->get_child_count() > 0) {
+ memdelete(actionsets_vb->get_child(0));
+ }
+
+ // in with the new...
+ if (action_map.is_valid()) {
+ Array action_sets = action_map->get_action_sets();
+ for (int i = 0; i < action_sets.size(); i++) {
+ Ref<OpenXRActionSet> action_set = action_sets[i];
+ _add_action_set_editor(action_set);
+ }
+ }
+}
+
+OpenXRInteractionProfileEditorBase *OpenXRActionMapEditor::_add_interaction_profile_editor(Ref<OpenXRInteractionProfile> p_interaction_profile) {
+ ERR_FAIL_COND_V(p_interaction_profile.is_null(), nullptr);
+
+ String profile_path = p_interaction_profile->get_interaction_profile_path();
+
+ // need to instance the correct editor for our profile
+ OpenXRInteractionProfileEditorBase *new_profile_editor = nullptr;
+ if (profile_path == "placeholder_text") {
+ // instance specific editor for this type
+ } else {
+ // instance generic editor
+ new_profile_editor = memnew(OpenXRInteractionProfileEditor(action_map, p_interaction_profile));
+ }
+
+ // now add it in..
+ ERR_FAIL_NULL_V(new_profile_editor, nullptr);
+ tabs->add_child(new_profile_editor);
+ new_profile_editor->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ tabs->set_tab_button_icon(tabs->get_tab_count() - 1, get_theme_icon(SNAME("close"), SNAME("TabBar")));
+
+ interaction_profiles.push_back(new_profile_editor);
+
+ return new_profile_editor;
+}
+
+void OpenXRActionMapEditor::_update_interaction_profiles() {
+ // out with the old...
+ while (interaction_profiles.size() > 0) {
+ Node *interaction_profile = interaction_profiles[0];
+ interaction_profiles.remove_at(0);
+
+ tabs->remove_child(interaction_profile);
+ interaction_profile->queue_delete();
+ }
+
+ // in with the new...
+ if (action_map.is_valid()) {
+ Array new_interaction_profiles = action_map->get_interaction_profiles();
+ for (int i = 0; i < new_interaction_profiles.size(); i++) {
+ Ref<OpenXRInteractionProfile> interaction_profile = new_interaction_profiles[i];
+ _add_interaction_profile_editor(interaction_profile);
+ }
+ }
+}
+
+OpenXRActionSetEditor *OpenXRActionMapEditor::_add_action_set(String p_name) {
+ ERR_FAIL_COND_V(action_map.is_null(), nullptr);
+ Ref<OpenXRActionSet> new_action_set;
+
+ // add our new action set
+ new_action_set.instantiate();
+ new_action_set->set_name(p_name);
+ new_action_set->set_localized_name(p_name);
+ action_map->add_action_set(new_action_set);
+
+ // update our editor right away
+ return _add_action_set_editor(new_action_set);
+}
+
+void OpenXRActionMapEditor::_remove_action_set(String p_name) {
+ ERR_FAIL_COND(action_map.is_null());
+ Ref<OpenXRActionSet> action_set = action_map->find_action_set(p_name);
+ ERR_FAIL_COND(action_set.is_null());
+
+ if (action_set->get_action_count() > 0) {
+ // we should remove these and add to our redo/undo step before calling _remove_action_set
+ WARN_PRINT("Action set still has associated actions before being removed!");
+ }
+
+ // now we remove it
+ action_map->remove_action_set(action_set);
+}
+
+void OpenXRActionMapEditor::_on_add_action_set() {
+ ERR_FAIL_COND(action_map.is_null());
+ String new_name = "New";
+ int count = 0;
+
+ while (action_map->find_action_set(new_name).is_valid()) {
+ new_name = "New_" + itos(count++);
+ }
+
+ OpenXRActionSetEditor *new_action_set_editor = _add_action_set(new_name);
+
+ // Make sure our action set is the current tab
+ tabs->set_current_tab(0);
+
+ call_deferred("_set_focus_on_action_set", new_action_set_editor);
+}
+
+void OpenXRActionMapEditor::_set_focus_on_action_set(OpenXRActionSetEditor *p_action_set_editor) {
+ // Scroll down to our new entry
+ actionsets_scroll->ensure_control_visible(p_action_set_editor);
+
+ // Set focus on this entry
+ p_action_set_editor->set_focus_on_entry();
+}
+
+void OpenXRActionMapEditor::_on_remove_action_set(Object *p_action_set_editor) {
+ ERR_FAIL_COND(action_map.is_null());
+
+ OpenXRActionSetEditor *action_set_editor = Object::cast_to<OpenXRActionSetEditor>(p_action_set_editor);
+ ERR_FAIL_NULL(action_set_editor);
+ ERR_FAIL_COND(action_set_editor->get_parent() != actionsets_vb);
+ Ref<OpenXRActionSet> action_set = action_set_editor->get_action_set();
+ ERR_FAIL_COND(action_set.is_null());
+
+ action_map->remove_action_set(action_set);
+ actionsets_vb->remove_child(action_set_editor);
+ action_set_editor->queue_delete();
+}
+
+void OpenXRActionMapEditor::_on_action_removed() {
+ // make sure our interaction profiles are updated
+ _update_interaction_profiles();
+}
+
+void OpenXRActionMapEditor::_on_add_interaction_profile() {
+ ERR_FAIL_COND(action_map.is_null());
+
+ PackedStringArray already_selected;
+
+ for (int i = 0; i < action_map->get_interaction_profile_count(); i++) {
+ already_selected.push_back(action_map->get_interaction_profile(i)->get_interaction_profile_path());
+ }
+
+ select_interaction_profile_dialog->open(already_selected);
+}
+
+void OpenXRActionMapEditor::_on_interaction_profile_selected(const String p_path) {
+ ERR_FAIL_COND(action_map.is_null());
+
+ Ref<OpenXRInteractionProfile> new_profile;
+ new_profile.instantiate();
+ new_profile->set_interaction_profile_path(p_path);
+ action_map->add_interaction_profile(new_profile);
+
+ _add_interaction_profile_editor(new_profile);
+
+ tabs->set_current_tab(tabs->get_tab_count() - 1);
+}
+
+void OpenXRActionMapEditor::_load_action_map(const String p_path, bool p_create_new_if_missing) {
+ action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
+ if (action_map.is_null()) {
+ if (p_create_new_if_missing) {
+ action_map.instantiate();
+ action_map->create_default_action_sets();
+ } else {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an OpenXR action map."));
+
+ edited_path = "";
+ header_label->set_text("");
+ return;
+ }
+ }
+
+ edited_path = p_path;
+ header_label->set_text(TTR("OpenXR Action map:") + " " + p_path.get_file());
+}
+
+void OpenXRActionMapEditor::_on_save_action_map() {
+ Error err = ResourceSaver::save(edited_path, action_map);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file: %s"), edited_path));
+ return;
+ }
+
+ _update_action_sets();
+ _update_interaction_profiles();
+}
+
+void OpenXRActionMapEditor::_on_reset_to_default_layout() {
+ // create a new one
+ action_map.unref();
+ action_map.instantiate();
+ action_map->create_default_action_sets();
+
+ _update_action_sets();
+ _update_interaction_profiles();
+}
+
+void OpenXRActionMapEditor::_on_tabs_tab_changed(int p_tab) {
+}
+
+void OpenXRActionMapEditor::_on_tab_button_pressed(int p_tab) {
+ OpenXRInteractionProfileEditorBase *profile_editor = static_cast<OpenXRInteractionProfileEditorBase *>(tabs->get_tab_control(p_tab));
+ ERR_FAIL_NULL(profile_editor);
+
+ Ref<OpenXRInteractionProfile> interaction_profile = profile_editor->get_interaction_profile();
+ ERR_FAIL_COND(interaction_profile.is_null());
+
+ action_map->remove_interaction_profile(interaction_profile);
+ tabs->remove_child(profile_editor);
+ profile_editor->queue_delete();
+}
+
+void OpenXRActionMapEditor::open_action_map(String p_path) {
+ EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+
+ _load_action_map(p_path);
+
+ _update_action_sets();
+ _update_interaction_profiles();
+}
+
+OpenXRActionMapEditor::OpenXRActionMapEditor() {
+ set_custom_minimum_size(Size2(0.0, 300.0));
+
+ top_hb = memnew(HBoxContainer);
+ add_child(top_hb);
+
+ header_label = memnew(Label);
+ header_label->set_text(String(TTR("Action Map")));
+ header_label->set_clip_text(true);
+ header_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ top_hb->add_child(header_label);
+
+ add_action_set = memnew(Button);
+ add_action_set->set_text(TTR("Add Action Set"));
+ add_action_set->set_tooltip(TTR("Add an action set."));
+ add_action_set->connect("pressed", callable_mp(this, &OpenXRActionMapEditor::_on_add_action_set));
+ top_hb->add_child(add_action_set);
+
+ add_interaction_profile = memnew(Button);
+ add_interaction_profile->set_text(TTR("Add profile"));
+ add_interaction_profile->set_tooltip(TTR("Add an interaction profile."));
+ add_interaction_profile->connect("pressed", callable_mp(this, &OpenXRActionMapEditor::_on_add_interaction_profile));
+ top_hb->add_child(add_interaction_profile);
+
+ VSeparator *vseparator = memnew(VSeparator);
+ top_hb->add_child(vseparator);
+
+ save_as = memnew(Button);
+ save_as->set_text(TTR("Save"));
+ save_as->set_tooltip(TTR("Save this OpenXR action map."));
+ save_as->connect("pressed", callable_mp(this, &OpenXRActionMapEditor::_on_save_action_map));
+ top_hb->add_child(save_as);
+
+ _default = memnew(Button);
+ _default->set_text(TTR("Reset to Default"));
+ _default->set_tooltip(TTR("Reset to default OpenXR action map."));
+ _default->connect("pressed", callable_mp(this, &OpenXRActionMapEditor::_on_reset_to_default_layout));
+ top_hb->add_child(_default);
+
+ tabs = memnew(TabContainer);
+ tabs->set_h_size_flags(SIZE_EXPAND_FILL);
+ tabs->set_v_size_flags(SIZE_EXPAND_FILL);
+ tabs->connect("tab_changed", callable_mp(this, &OpenXRActionMapEditor::_on_tabs_tab_changed));
+ tabs->connect("tab_button_pressed", callable_mp(this, &OpenXRActionMapEditor::_on_tab_button_pressed));
+ add_child(tabs);
+
+ actionsets_scroll = memnew(ScrollContainer);
+ actionsets_scroll->set_h_size_flags(SIZE_EXPAND_FILL);
+ actionsets_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
+ actionsets_scroll->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+ tabs->add_child(actionsets_scroll);
+ actionsets_scroll->set_name(TTR("Action Sets"));
+
+ actionsets_vb = memnew(VBoxContainer);
+ actionsets_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ actionsets_scroll->add_child(actionsets_vb);
+
+ select_interaction_profile_dialog = memnew(OpenXRSelectInteractionProfileDialog);
+ select_interaction_profile_dialog->connect("interaction_profile_selected", callable_mp(this, &OpenXRActionMapEditor::_on_interaction_profile_selected));
+ add_child(select_interaction_profile_dialog);
+
+ _load_action_map(ProjectSettings::get_singleton()->get("xr/openxr/default_action_map"));
+}
+
+OpenXRActionMapEditor::~OpenXRActionMapEditor() {
+}
diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h
new file mode 100644
index 0000000000..dfc941b500
--- /dev/null
+++ b/modules/openxr/editor/openxr_action_map_editor.h
@@ -0,0 +1,100 @@
+/*************************************************************************/
+/* openxr_action_map_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OPENXR_ACTION_MAP_EDITOR_H
+#define OPENXR_ACTION_MAP_EDITOR_H
+
+#include "../action_map/openxr_action_map.h"
+#include "../editor/openxr_action_set_editor.h"
+#include "../editor/openxr_interaction_profile_editor.h"
+#include "../editor/openxr_select_interaction_profile_dialog.h"
+
+#include "editor/editor_plugin.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/label.h"
+#include "scene/gui/scroll_container.h"
+#include "scene/gui/tab_container.h"
+
+class OpenXRActionMapEditor : public VBoxContainer {
+ GDCLASS(OpenXRActionMapEditor, VBoxContainer);
+
+private:
+ String edited_path;
+ Ref<OpenXRActionMap> action_map;
+ Vector<Node *> interaction_profiles;
+
+ HBoxContainer *top_hb = nullptr;
+ Label *header_label = nullptr;
+ Button *add_action_set = nullptr;
+ Button *add_interaction_profile = nullptr;
+ Button *load = nullptr;
+ Button *save_as = nullptr;
+ Button *_default = nullptr;
+ TabContainer *tabs = nullptr;
+ ScrollContainer *actionsets_scroll = nullptr;
+ VBoxContainer *actionsets_vb = nullptr;
+ OpenXRSelectInteractionProfileDialog *select_interaction_profile_dialog = nullptr;
+
+ OpenXRActionSetEditor *_add_action_set_editor(Ref<OpenXRActionSet> p_action_set);
+ void _update_action_sets();
+ OpenXRInteractionProfileEditorBase *_add_interaction_profile_editor(Ref<OpenXRInteractionProfile> p_interaction_profile);
+ void _update_interaction_profiles();
+
+ OpenXRActionSetEditor *_add_action_set(String p_name);
+ void _remove_action_set(String p_name);
+
+ void _on_add_action_set();
+ void _set_focus_on_action_set(OpenXRActionSetEditor *p_action_set_editor);
+ void _on_remove_action_set(Object *p_action_set_editor);
+ void _on_action_removed();
+
+ void _on_add_interaction_profile();
+ void _on_interaction_profile_selected(const String p_path);
+
+ void _load_action_map(const String p_path, bool p_create_new_if_missing = false);
+ void _on_save_action_map();
+ void _on_reset_to_default_layout();
+
+ void _on_tabs_tab_changed(int p_tab);
+ void _on_tab_button_pressed(int p_tab);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ void open_action_map(String p_path);
+
+ OpenXRActionMapEditor();
+ ~OpenXRActionMapEditor();
+};
+
+#endif // !OPENXR_ACTION_MAP_EDITOR_H
diff --git a/modules/openxr/editor/openxr_action_set_editor.cpp b/modules/openxr/editor/openxr_action_set_editor.cpp
new file mode 100644
index 0000000000..7bf8557c5b
--- /dev/null
+++ b/modules/openxr/editor/openxr_action_set_editor.cpp
@@ -0,0 +1,218 @@
+/*************************************************************************/
+/* openxr_action_set_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "openxr_action_set_editor.h"
+#include "openxr_action_editor.h"
+
+void OpenXRActionSetEditor::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_set_editor")));
+ ADD_SIGNAL(MethodInfo("action_removed"));
+}
+
+void OpenXRActionSetEditor::_set_fold_icon() {
+ if (is_expanded) {
+ fold_btn->set_icon(get_theme_icon(SNAME("GuiTreeArrowDown"), SNAME("EditorIcons")));
+ } else {
+ fold_btn->set_icon(get_theme_icon(SNAME("GuiTreeArrowRight"), SNAME("EditorIcons")));
+ }
+}
+
+void OpenXRActionSetEditor::_theme_changed() {
+ _set_fold_icon();
+ add_action->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ rem_action_set->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+}
+
+void OpenXRActionSetEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ _theme_changed();
+ panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
+ } break;
+ }
+}
+
+OpenXRActionEditor *OpenXRActionSetEditor::_add_action_editor(Ref<OpenXRAction> p_action) {
+ OpenXRActionEditor *action_editor = memnew(OpenXRActionEditor(p_action));
+ action_editor->connect("remove", callable_mp(this, &OpenXRActionSetEditor::_on_remove_action));
+ actions_vb->add_child(action_editor);
+
+ return action_editor;
+}
+
+void OpenXRActionSetEditor::_update_actions() {
+ // out with the old...
+ while (actions_vb->get_child_count() > 0) {
+ memdelete(actions_vb->get_child(0));
+ }
+
+ // in with the new...
+ Array actions = action_set->get_actions();
+ for (int i = 0; i < actions.size(); i++) {
+ Ref<OpenXRAction> action = actions[i];
+ _add_action_editor(action);
+ }
+}
+
+void OpenXRActionSetEditor::_on_toggle_expand() {
+ is_expanded = !is_expanded;
+ actions_vb->set_visible(is_expanded);
+ _set_fold_icon();
+}
+
+void OpenXRActionSetEditor::_on_action_set_name_changed(const String p_new_text) {
+ // TODO validate if entry is allowed
+
+ // If our localized name matches our action set name, set this too
+ if (action_set->get_name() == action_set->get_localized_name()) {
+ action_set->set_localized_name(p_new_text);
+ action_set_localized_name->set_text(p_new_text);
+ }
+ action_set->set_name(p_new_text);
+}
+
+void OpenXRActionSetEditor::_on_action_set_localized_name_changed(const String p_new_text) {
+ action_set->set_localized_name(p_new_text);
+}
+
+void OpenXRActionSetEditor::_on_action_set_priority_changed(const String p_new_text) {
+ int64_t value = p_new_text.to_int();
+
+ action_set->set_priority(value);
+}
+
+void OpenXRActionSetEditor::_on_add_action() {
+ Ref<OpenXRAction> new_action;
+
+ new_action.instantiate();
+ new_action->set_name("New");
+ new_action->set_localized_name("New");
+ action_set->add_action(new_action);
+
+ _add_action_editor(new_action);
+
+ // TODO handle focus
+}
+
+void OpenXRActionSetEditor::_on_remove_action_set() {
+ emit_signal("remove", this);
+}
+
+void OpenXRActionSetEditor::_on_remove_action(Object *p_action_editor) {
+ OpenXRActionEditor *action_editor = Object::cast_to<OpenXRActionEditor>(p_action_editor);
+ ERR_FAIL_NULL(action_editor);
+ ERR_FAIL_COND(action_editor->get_parent() != actions_vb);
+ Ref<OpenXRAction> action = action_editor->get_action();
+ ERR_FAIL_COND(action.is_null());
+
+ // TODO add undo/redo action
+
+ // TODO find where this action is used by our interaction profiles and remove it there
+
+ // And remove it....
+ action_map->remove_action(action->get_name_with_set()); // remove it from the set and any interaction profile it relates to
+ actions_vb->remove_child(action_editor);
+ action_editor->queue_delete();
+
+ // Let action map editor know so we can update our interaction profiles
+ emit_signal("action_removed");
+}
+
+void OpenXRActionSetEditor::set_focus_on_entry() {
+ ERR_FAIL_NULL(action_set_name);
+ action_set_name->grab_focus();
+}
+
+OpenXRActionSetEditor::OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set) {
+ action_map = p_action_map;
+ action_set = p_action_set;
+
+ set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ panel = memnew(PanelContainer);
+ panel->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ add_child(panel);
+
+ HBoxContainer *panel_hb = memnew(HBoxContainer);
+ panel_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ panel->add_child(panel_hb);
+
+ fold_btn = memnew(Button);
+ fold_btn->set_v_size_flags(Control::SIZE_SHRINK_BEGIN);
+ fold_btn->connect("pressed", callable_mp(this, &OpenXRActionSetEditor::_on_toggle_expand));
+ fold_btn->set_flat(true);
+ panel_hb->add_child(fold_btn);
+
+ main_vb = memnew(VBoxContainer);
+ main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ panel_hb->add_child(main_vb);
+
+ action_set_hb = memnew(HBoxContainer);
+ action_set_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ main_vb->add_child(action_set_hb);
+
+ action_set_name = memnew(LineEdit);
+ action_set_name->set_text(action_set->get_name());
+ action_set_name->set_custom_minimum_size(Size2(150.0, 0.0));
+ action_set_name->connect("text_changed", callable_mp(this, &OpenXRActionSetEditor::_on_action_set_name_changed));
+ action_set_hb->add_child(action_set_name);
+
+ action_set_localized_name = memnew(LineEdit);
+ action_set_localized_name->set_text(action_set->get_localized_name());
+ action_set_localized_name->set_custom_minimum_size(Size2(150.0, 0.0));
+ action_set_localized_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ action_set_localized_name->connect("text_changed", callable_mp(this, &OpenXRActionSetEditor::_on_action_set_localized_name_changed));
+ action_set_hb->add_child(action_set_localized_name);
+
+ action_set_priority = memnew(TextEdit);
+ action_set_priority->set_text(itos(action_set->get_priority()));
+ action_set_priority->set_custom_minimum_size(Size2(50.0, 0.0));
+ action_set_priority->connect("text_changed", callable_mp(this, &OpenXRActionSetEditor::_on_action_set_priority_changed));
+ action_set_hb->add_child(action_set_priority);
+
+ add_action = memnew(Button);
+ add_action->set_tooltip("Add Action.");
+ add_action->connect("pressed", callable_mp(this, &OpenXRActionSetEditor::_on_add_action));
+ add_action->set_flat(true);
+ action_set_hb->add_child(add_action);
+
+ rem_action_set = memnew(Button);
+ rem_action_set->set_tooltip("Remove Action Set.");
+ rem_action_set->connect("pressed", callable_mp(this, &OpenXRActionSetEditor::_on_remove_action_set));
+ rem_action_set->set_flat(true);
+ action_set_hb->add_child(rem_action_set);
+
+ actions_vb = memnew(VBoxContainer);
+ actions_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ main_vb->add_child(actions_vb);
+
+ _update_actions();
+}
diff --git a/modules/openxr/editor/openxr_action_set_editor.h b/modules/openxr/editor/openxr_action_set_editor.h
new file mode 100644
index 0000000000..f3960dcbf9
--- /dev/null
+++ b/modules/openxr/editor/openxr_action_set_editor.h
@@ -0,0 +1,88 @@
+/*************************************************************************/
+/* openxr_action_set_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OPENXR_ACTION_SET_EDITOR_H
+#define OPENXR_ACTION_SET_EDITOR_H
+
+#include "../action_map/openxr_action_map.h"
+#include "../action_map/openxr_action_set.h"
+#include "openxr_action_editor.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/panel_container.h"
+#include "scene/gui/text_edit.h"
+
+class OpenXRActionSetEditor : public HBoxContainer {
+ GDCLASS(OpenXRActionSetEditor, HBoxContainer);
+
+private:
+ Ref<OpenXRActionMap> action_map;
+ Ref<OpenXRActionSet> action_set;
+
+ bool is_expanded = true;
+
+ PanelContainer *panel = nullptr;
+ Button *fold_btn = nullptr;
+ VBoxContainer *main_vb = nullptr;
+ HBoxContainer *action_set_hb = nullptr;
+ LineEdit *action_set_name = nullptr;
+ LineEdit *action_set_localized_name = nullptr;
+ TextEdit *action_set_priority = nullptr;
+ Button *add_action = nullptr;
+ Button *rem_action_set = nullptr;
+ VBoxContainer *actions_vb = nullptr;
+
+ void _set_fold_icon();
+ void _theme_changed();
+ OpenXRActionEditor *_add_action_editor(Ref<OpenXRAction> p_action);
+ void _update_actions();
+
+ void _on_toggle_expand();
+ void _on_action_set_name_changed(const String p_new_text);
+ void _on_action_set_localized_name_changed(const String p_new_text);
+ void _on_action_set_priority_changed(const String p_new_text);
+ void _on_add_action();
+ void _on_remove_action_set();
+
+ void _on_remove_action(Object *p_action_editor);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ Ref<OpenXRActionSet> get_action_set() { return action_set; };
+ void set_focus_on_entry();
+
+ OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set);
+};
+
+#endif // !OPENXR_ACTION_SET_EDITOR_H
diff --git a/modules/openxr/editor/openxr_editor_plugin.cpp b/modules/openxr/editor/openxr_editor_plugin.cpp
new file mode 100644
index 0000000000..b87b538511
--- /dev/null
+++ b/modules/openxr/editor/openxr_editor_plugin.cpp
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* openxr_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "openxr_editor_plugin.h"
+
+#include "../action_map/openxr_action_map.h"
+#include "editor/editor_node.h"
+
+void OpenXREditorPlugin::edit(Object *p_node) {
+ if (Object::cast_to<OpenXRActionMap>(p_node)) {
+ String path = Object::cast_to<OpenXRActionMap>(p_node)->get_path();
+ if (path.is_resource_file()) {
+ action_map_editor->open_action_map(path);
+ }
+ }
+}
+
+bool OpenXREditorPlugin::handles(Object *p_node) const {
+ return (Object::cast_to<OpenXRActionMap>(p_node) != nullptr);
+}
+
+void OpenXREditorPlugin::make_visible(bool p_visible) {
+}
+
+OpenXREditorPlugin::OpenXREditorPlugin() {
+ action_map_editor = memnew(OpenXRActionMapEditor);
+ EditorNode::get_singleton()->add_bottom_panel_item(TTR("OpenXR Action Map"), action_map_editor);
+}
+
+OpenXREditorPlugin::~OpenXREditorPlugin() {
+}
diff --git a/modules/openxr/editor/openxr_editor_plugin.h b/modules/openxr/editor/openxr_editor_plugin.h
new file mode 100644
index 0000000000..9d04bc4e45
--- /dev/null
+++ b/modules/openxr/editor/openxr_editor_plugin.h
@@ -0,0 +1,53 @@
+/*************************************************************************/
+/* openxr_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OPENXR_EDITOR_PLUGIN_H
+#define OPENXR_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+#include "openxr_action_map_editor.h"
+
+class OpenXREditorPlugin : public EditorPlugin {
+ GDCLASS(OpenXREditorPlugin, EditorPlugin);
+
+ OpenXRActionMapEditor *action_map_editor;
+
+public:
+ virtual String get_name() const override { return "OpenXRPlugin"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_node) override;
+ virtual bool handles(Object *p_node) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ OpenXREditorPlugin();
+ ~OpenXREditorPlugin();
+};
+
+#endif // !OPENXR_EDITOR_PLUGIN_H
diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.cpp b/modules/openxr/editor/openxr_interaction_profile_editor.cpp
new file mode 100644
index 0000000000..669cc694f2
--- /dev/null
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.cpp
@@ -0,0 +1,272 @@
+/*************************************************************************/
+/* openxr_interaction_profile_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "openxr_interaction_profile_editor.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/label.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/panel_container.h"
+#include "scene/gui/separator.h"
+#include "scene/gui/text_edit.h"
+
+///////////////////////////////////////////////////////////////////////////
+// Interaction profile editor base
+
+void OpenXRInteractionProfileEditorBase::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_add_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_add_binding);
+ ClassDB::bind_method(D_METHOD("_remove_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_remove_binding);
+ ClassDB::bind_method(D_METHOD("_update_interaction_profile"), &OpenXRInteractionProfileEditorBase::_update_interaction_profile);
+}
+
+void OpenXRInteractionProfileEditorBase::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ _update_interaction_profile();
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
+ _theme_changed();
+ } break;
+ }
+}
+
+void OpenXRInteractionProfileEditorBase::_add_binding(const String p_action, const String p_path) {
+ ERR_FAIL_COND(action_map.is_null());
+ ERR_FAIL_COND(interaction_profile.is_null());
+
+ Ref<OpenXRAction> action = action_map->get_action(p_action);
+ ERR_FAIL_COND(action.is_null());
+
+ Ref<OpenXRIPBinding> binding = interaction_profile->get_binding_for_action(action);
+ if (binding.is_null()) {
+ // create a new binding
+ binding.instantiate();
+ binding->set_action(action);
+ interaction_profile->add_binding(binding);
+ }
+
+ binding->add_path(p_path);
+
+ // Update our toplevel paths
+ action->set_toplevel_paths(action_map->get_top_level_paths(action));
+
+ call_deferred("_update_interaction_profile");
+}
+
+void OpenXRInteractionProfileEditorBase::_remove_binding(const String p_action, const String p_path) {
+ ERR_FAIL_COND(action_map.is_null());
+ ERR_FAIL_COND(interaction_profile.is_null());
+
+ Ref<OpenXRAction> action = action_map->get_action(p_action);
+ ERR_FAIL_COND(action.is_null());
+
+ Ref<OpenXRIPBinding> binding = interaction_profile->get_binding_for_action(action);
+ if (binding.is_valid()) {
+ binding->remove_path(p_path);
+
+ if (binding->get_path_count() == 0) {
+ interaction_profile->remove_binding(binding);
+ }
+
+ // Update our toplevel paths
+ action->set_toplevel_paths(action_map->get_top_level_paths(action));
+
+ call_deferred("_update_interaction_profile");
+ }
+}
+
+OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
+ action_map = p_action_map;
+ interaction_profile = p_interaction_profile;
+ String profile_path = interaction_profile->get_interaction_profile_path();
+ String profile_name = profile_path;
+
+ profile_def = OpenXRDefs::get_profile(profile_path);
+ if (profile_def != nullptr) {
+ profile_name = profile_def->display_name;
+ }
+
+ set_name(profile_name);
+ set_h_size_flags(SIZE_EXPAND_FILL);
+ set_v_size_flags(SIZE_EXPAND_FILL);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Default interaction profile editor
+
+void OpenXRInteractionProfileEditor::select_action_for(const String p_io_path) {
+ selecting_for_io_path = p_io_path;
+ select_action_dialog->open();
+}
+
+void OpenXRInteractionProfileEditor::action_selected(const String p_action) {
+ _add_binding(p_action, selecting_for_io_path);
+ selecting_for_io_path = "";
+}
+
+void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, const OpenXRDefs::IOPath *p_io_path) {
+ HBoxContainer *path_hb = memnew(HBoxContainer);
+ path_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ p_container->add_child(path_hb);
+
+ Label *path_label = memnew(Label);
+ path_label->set_text(p_io_path->display_name);
+ path_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ path_hb->add_child(path_label);
+
+ Label *type_label = memnew(Label);
+ switch (p_io_path->action_type) {
+ case OpenXRAction::OPENXR_ACTION_BOOL: {
+ type_label->set_text(TTR("Boolean"));
+ } break;
+ case OpenXRAction::OPENXR_ACTION_FLOAT: {
+ type_label->set_text(TTR("Float"));
+ } break;
+ case OpenXRAction::OPENXR_ACTION_VECTOR2: {
+ type_label->set_text(TTR("Vector2"));
+ } break;
+ case OpenXRAction::OPENXR_ACTION_POSE: {
+ type_label->set_text(TTR("Pose"));
+ } break;
+ case OpenXRAction::OPENXR_ACTION_HAPTIC: {
+ type_label->set_text(TTR("Haptic"));
+ } break;
+ default: {
+ type_label->set_text(TTR("Unknown"));
+ } break;
+ }
+ type_label->set_custom_minimum_size(Size2(50.0, 0.0));
+ path_hb->add_child(type_label);
+
+ Button *path_add = memnew(Button);
+ path_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ path_add->set_flat(true);
+ Vector<Variant> add_binds;
+ add_binds.push_back(String(p_io_path->openxr_path));
+ path_add->connect("pressed", callable_mp(this, &OpenXRInteractionProfileEditor::select_action_for), add_binds);
+ path_hb->add_child(path_add);
+
+ if (interaction_profile.is_valid()) {
+ String io_path = String(p_io_path->openxr_path);
+ Array bindings = interaction_profile->get_bindings();
+ for (int i = 0; i < bindings.size(); i++) {
+ Ref<OpenXRIPBinding> binding = bindings[i];
+ if (binding->has_path(io_path)) {
+ Ref<OpenXRAction> action = binding->get_action();
+
+ HBoxContainer *action_hb = memnew(HBoxContainer);
+ action_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ p_container->add_child(action_hb);
+
+ Control *indent_node = memnew(Control);
+ indent_node->set_custom_minimum_size(Size2(10.0, 0.0));
+ action_hb->add_child(indent_node);
+
+ Label *action_label = memnew(Label);
+ action_label->set_text(action->get_name_with_set() + ": " + action->get_localized_name());
+ action_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ action_hb->add_child(action_label);
+
+ Button *action_rem = memnew(Button);
+ action_rem->set_flat(true);
+ action_rem->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+ Vector<Variant> remove_binds;
+ remove_binds.push_back(action->get_name_with_set());
+ remove_binds.push_back(String(p_io_path->openxr_path));
+ action_rem->connect("pressed", callable_mp((OpenXRInteractionProfileEditorBase *)this, &OpenXRInteractionProfileEditorBase::_remove_binding), remove_binds);
+ action_hb->add_child(action_rem);
+ }
+ }
+ }
+}
+
+void OpenXRInteractionProfileEditor::_update_interaction_profile() {
+ // out with the old...
+ while (main_hb->get_child_count() > 0) {
+ memdelete(main_hb->get_child(0));
+ }
+
+ // in with the new...
+
+ // Determine toplevel paths
+ Vector<const OpenXRDefs::TopLevelPath *> top_level_paths;
+ for (int i = 0; i < profile_def->io_path_count; i++) {
+ const OpenXRDefs::IOPath *io_path = &profile_def->io_paths[i];
+
+ if (!top_level_paths.has(io_path->top_level_path)) {
+ top_level_paths.push_back(io_path->top_level_path);
+ }
+ }
+
+ for (int i = 0; i < top_level_paths.size(); i++) {
+ PanelContainer *panel = memnew(PanelContainer);
+ panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ main_hb->add_child(panel);
+ panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
+
+ VBoxContainer *container = memnew(VBoxContainer);
+ panel->add_child(container);
+
+ Label *label = memnew(Label);
+ label->set_text(top_level_paths[i]->display_name);
+ container->add_child(label);
+
+ for (int j = 0; j < profile_def->io_path_count; j++) {
+ const OpenXRDefs::IOPath *io_path = &profile_def->io_paths[j];
+ if (io_path->top_level_path == top_level_paths[i]) {
+ _add_io_path(container, io_path);
+ }
+ }
+ }
+}
+
+void OpenXRInteractionProfileEditor::_theme_changed() {
+ for (int i = 0; i < main_hb->get_child_count(); i++) {
+ Control *panel = static_cast<Control *>(main_hb->get_child(i));
+ if (panel) {
+ panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
+ }
+ }
+}
+
+OpenXRInteractionProfileEditor::OpenXRInteractionProfileEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) :
+ OpenXRInteractionProfileEditorBase(p_action_map, p_interaction_profile) {
+ // TODO background of scrollbox should be darker with our VBoxContainers we're adding in _update_interaction_profile the normal color
+
+ main_hb = memnew(HBoxContainer);
+ add_child(main_hb);
+
+ select_action_dialog = memnew(OpenXRSelectActionDialog(p_action_map));
+ select_action_dialog->connect("action_selected", callable_mp(this, &OpenXRInteractionProfileEditor::action_selected));
+ add_child(select_action_dialog);
+
+ _update_interaction_profile();
+}
diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.h b/modules/openxr/editor/openxr_interaction_profile_editor.h
new file mode 100644
index 0000000000..f50da1a003
--- /dev/null
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.h
@@ -0,0 +1,83 @@
+/*************************************************************************/
+/* openxr_interaction_profile_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OPENXR_INTERACTION_PROFILE_EDITOR_H
+#define OPENXR_INTERACTION_PROFILE_EDITOR_H
+
+#include "../action_map/openxr_action_map.h"
+#include "../action_map/openxr_defs.h"
+#include "../action_map/openxr_interaction_profile.h"
+#include "scene/gui/scroll_container.h"
+
+#include "openxr_select_action_dialog.h"
+
+class OpenXRInteractionProfileEditorBase : public ScrollContainer {
+ GDCLASS(OpenXRInteractionProfileEditorBase, ScrollContainer);
+
+protected:
+ Ref<OpenXRInteractionProfile> interaction_profile;
+ Ref<OpenXRActionMap> action_map;
+
+ static void _bind_methods();
+ void _notification(int p_what);
+
+ const OpenXRDefs::InteractionProfile *profile_def = nullptr;
+
+public:
+ Ref<OpenXRInteractionProfile> get_interaction_profile() { return interaction_profile; }
+
+ virtual void _update_interaction_profile() {}
+ virtual void _theme_changed() {}
+ void _add_binding(const String p_action, const String p_path);
+ void _remove_binding(const String p_action, const String p_path);
+
+ OpenXRInteractionProfileEditorBase(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile);
+};
+
+class OpenXRInteractionProfileEditor : public OpenXRInteractionProfileEditorBase {
+ GDCLASS(OpenXRInteractionProfileEditor, OpenXRInteractionProfileEditorBase);
+
+private:
+ String selecting_for_io_path;
+ HBoxContainer *main_hb = nullptr;
+ OpenXRSelectActionDialog *select_action_dialog = nullptr;
+
+ void _add_io_path(VBoxContainer *p_container, const OpenXRDefs::IOPath *p_io_path);
+
+public:
+ void select_action_for(const String p_io_path);
+ void action_selected(const String p_action);
+
+ virtual void _update_interaction_profile() override;
+ virtual void _theme_changed() override;
+ OpenXRInteractionProfileEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile);
+};
+
+#endif // !OPENXR_INTERACTION_PROFILE_EDITOR_H
diff --git a/modules/openxr/editor/openxr_select_action_dialog.cpp b/modules/openxr/editor/openxr_select_action_dialog.cpp
new file mode 100644
index 0000000000..c2a2965200
--- /dev/null
+++ b/modules/openxr/editor/openxr_select_action_dialog.cpp
@@ -0,0 +1,135 @@
+/*************************************************************************/
+/* openxr_select_action_dialog.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "openxr_select_action_dialog.h"
+#include "editor/editor_node.h"
+
+void OpenXRSelectActionDialog::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("action_selected", PropertyInfo(Variant::STRING, "action")));
+}
+
+void OpenXRSelectActionDialog::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ } break;
+ }
+}
+
+void OpenXRSelectActionDialog::_on_select_action(const String p_action) {
+ if (selected_action != "") {
+ NodePath button_path = action_buttons[selected_action];
+ Button *button = static_cast<Button *>(get_node(button_path));
+ if (button != nullptr) {
+ button->set_flat(true);
+ }
+ }
+
+ selected_action = p_action;
+
+ if (selected_action != "") {
+ NodePath button_path = action_buttons[selected_action];
+ Button *button = static_cast<Button *>(get_node(button_path));
+ if (button != nullptr) {
+ button->set_flat(false);
+ }
+ }
+}
+
+void OpenXRSelectActionDialog::open() {
+ ERR_FAIL_COND(action_map.is_null());
+
+ // out with the old...
+ while (main_vb->get_child_count() > 0) {
+ memdelete(main_vb->get_child(0));
+ }
+
+ selected_action = "";
+ action_buttons.clear();
+
+ Array action_sets = action_map->get_action_sets();
+ for (int i = 0; i < action_sets.size(); i++) {
+ Ref<OpenXRActionSet> action_set = action_sets[i];
+
+ Label *action_set_label = memnew(Label);
+ action_set_label->set_text(action_set->get_localized_name());
+ main_vb->add_child(action_set_label);
+
+ Array actions = action_set->get_actions();
+ for (int j = 0; j < actions.size(); j++) {
+ Ref<OpenXRAction> action = actions[j];
+
+ HBoxContainer *action_hb = memnew(HBoxContainer);
+ main_vb->add_child(action_hb);
+
+ Control *indent_node = memnew(Control);
+ indent_node->set_custom_minimum_size(Size2(10.0, 0.0));
+ action_hb->add_child(indent_node);
+
+ Button *action_button = memnew(Button);
+ String action_name = action->get_name_with_set();
+ Vector<Variant> binds;
+ binds.push_back(action_name);
+ action_button->set_flat(true);
+ action_button->set_text(action->get_name() + ": " + action->get_localized_name());
+ action_button->connect("pressed", callable_mp(this, &OpenXRSelectActionDialog::_on_select_action), binds);
+ action_hb->add_child(action_button);
+
+ action_buttons[action_name] = action_button->get_path();
+ }
+ }
+
+ popup_centered();
+}
+
+void OpenXRSelectActionDialog::ok_pressed() {
+ if (selected_action == "") {
+ return;
+ }
+
+ emit_signal("action_selected", selected_action);
+
+ hide();
+}
+
+OpenXRSelectActionDialog::OpenXRSelectActionDialog(Ref<OpenXRActionMap> p_action_map) {
+ action_map = p_action_map;
+
+ set_title(TTR("Select an action"));
+
+ scroll = memnew(ScrollContainer);
+ scroll->set_custom_minimum_size(Size2(600.0, 400.0));
+ add_child(scroll);
+
+ main_vb = memnew(VBoxContainer);
+ main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ scroll->add_child(main_vb);
+}
diff --git a/modules/openxr/editor/openxr_select_action_dialog.h b/modules/openxr/editor/openxr_select_action_dialog.h
new file mode 100644
index 0000000000..ea2c30373b
--- /dev/null
+++ b/modules/openxr/editor/openxr_select_action_dialog.h
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* openxr_select_action_dialog.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OPENXR_SELECT_ACTION_DIALOG_H
+#define OPENXR_SELECT_ACTION_DIALOG_H
+
+#include "../action_map/openxr_action_map.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/label.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/scroll_container.h"
+#include "scene/gui/separator.h"
+#include "scene/gui/text_edit.h"
+
+class OpenXRSelectActionDialog : public ConfirmationDialog {
+ GDCLASS(OpenXRSelectActionDialog, ConfirmationDialog);
+
+private:
+ Ref<OpenXRActionMap> action_map;
+ String selected_action;
+ Dictionary action_buttons;
+
+ VBoxContainer *main_vb = nullptr;
+ ScrollContainer *scroll = nullptr;
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ void _on_select_action(const String p_action);
+ void open();
+ virtual void ok_pressed() override;
+
+ OpenXRSelectActionDialog(Ref<OpenXRActionMap> p_action_map);
+};
+
+#endif // !OPENXR_SELECT_ACTION_DIALOG_H
diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
new file mode 100644
index 0000000000..12b110f146
--- /dev/null
+++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
@@ -0,0 +1,125 @@
+/*************************************************************************/
+/* openxr_select_interaction_profile_dialog.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "openxr_select_interaction_profile_dialog.h"
+
+void OpenXRSelectInteractionProfileDialog::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("interaction_profile_selected", PropertyInfo(Variant::STRING, "interaction_profile")));
+}
+
+void OpenXRSelectInteractionProfileDialog::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ } break;
+ }
+}
+
+void OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile(const String p_interaction_profile) {
+ if (selected_interaction_profile != "") {
+ NodePath button_path = ip_buttons[selected_interaction_profile];
+ Button *button = static_cast<Button *>(get_node(button_path));
+ if (button != nullptr) {
+ button->set_flat(true);
+ }
+ }
+
+ selected_interaction_profile = p_interaction_profile;
+
+ if (selected_interaction_profile != "") {
+ NodePath button_path = ip_buttons[selected_interaction_profile];
+ Button *button = static_cast<Button *>(get_node(button_path));
+ if (button != nullptr) {
+ button->set_flat(false);
+ }
+ }
+}
+
+void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_include) {
+ int available_count = 0;
+
+ // out with the old...
+ while (main_vb->get_child_count() > 0) {
+ memdelete(main_vb->get_child(0));
+ }
+
+ selected_interaction_profile = "";
+ ip_buttons.clear();
+
+ // in with the new
+ PackedStringArray interaction_profiles = OpenXRDefs::get_interaction_profile_paths();
+ for (int i = 0; i < interaction_profiles.size(); i++) {
+ String path = interaction_profiles[i];
+ if (!p_do_not_include.has(path)) {
+ Button *ip_button = memnew(Button);
+ Vector<Variant> binds;
+ binds.push_back(path);
+ ip_button->set_flat(true);
+ ip_button->set_text(OpenXRDefs::get_profile(path)->display_name);
+ ip_button->connect("pressed", callable_mp(this, &OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile), binds);
+ main_vb->add_child(ip_button);
+
+ ip_buttons[path] = ip_button->get_path();
+ available_count++;
+ }
+ }
+
+ if (available_count == 0) {
+ // give warning that we have all profiles selected
+
+ } else {
+ // TODO maybe if we only have one, auto select it?
+
+ popup_centered();
+ }
+}
+
+void OpenXRSelectInteractionProfileDialog::ok_pressed() {
+ if (selected_interaction_profile == "") {
+ return;
+ }
+
+ emit_signal("interaction_profile_selected", selected_interaction_profile);
+
+ hide();
+}
+
+OpenXRSelectInteractionProfileDialog::OpenXRSelectInteractionProfileDialog() {
+ set_title("Select an interaction profile");
+
+ scroll = memnew(ScrollContainer);
+ scroll->set_custom_minimum_size(Size2(600.0, 400.0));
+ add_child(scroll);
+
+ main_vb = memnew(VBoxContainer);
+ // main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ scroll->add_child(main_vb);
+}
diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.h b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
new file mode 100644
index 0000000000..d177861ff3
--- /dev/null
+++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* openxr_select_interaction_profile_dialog.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H
+#define OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H
+
+#include "../action_map/openxr_defs.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/label.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/scroll_container.h"
+#include "scene/gui/separator.h"
+#include "scene/gui/text_edit.h"
+
+class OpenXRSelectInteractionProfileDialog : public ConfirmationDialog {
+ GDCLASS(OpenXRSelectInteractionProfileDialog, ConfirmationDialog);
+
+private:
+ String selected_interaction_profile;
+ Dictionary ip_buttons;
+
+ VBoxContainer *main_vb = nullptr;
+ ScrollContainer *scroll = nullptr;
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ void _on_select_interaction_profile(const String p_interaction_profile);
+ void open(PackedStringArray p_do_not_include);
+ virtual void ok_pressed() override;
+
+ OpenXRSelectInteractionProfileDialog();
+};
+
+#endif // !OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H