initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_openxr")
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj

View File

@@ -0,0 +1,171 @@
/**************************************************************************/
/* openxr_action_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
void OpenXRActionEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_do_set_name", "name"), &OpenXRActionEditor::_do_set_name);
ClassDB::bind_method(D_METHOD("_do_set_localized_name", "name"), &OpenXRActionEditor::_do_set_localized_name);
ClassDB::bind_method(D_METHOD("_do_set_action_type", "type"), &OpenXRActionEditor::_do_set_action_type);
ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_editor")));
}
void OpenXRActionEditor::_theme_changed() {
rem_action->set_button_icon(get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
}
void OpenXRActionEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
_theme_changed();
} break;
}
}
void OpenXRActionEditor::_on_action_name_changed(const String p_new_text) {
if (action->get_name() != p_new_text) {
undo_redo->create_action(TTR("Rename Action"));
undo_redo->add_do_method(this, "_do_set_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_name", action->get_name());
undo_redo->commit_action(false);
// If our localized name matches our action name, set this too
if (action->get_name() == action->get_localized_name()) {
undo_redo->create_action(TTR("Rename Actions Localized name"));
undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_localized_name", action->get_localized_name());
undo_redo->commit_action(false);
action->set_localized_name(p_new_text);
action_localized_name->set_text(p_new_text);
}
action->set_name(p_new_text);
action->set_edited(true);
}
}
void OpenXRActionEditor::_do_set_name(const String p_new_text) {
action->set_name(p_new_text);
action->set_edited(true);
action_name->set_text(p_new_text);
}
void OpenXRActionEditor::_on_action_localized_name_changed(const String p_new_text) {
if (action->get_localized_name() != p_new_text) {
undo_redo->create_action(TTR("Rename Actions Localized name"));
undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_localized_name", action->get_localized_name());
undo_redo->commit_action(false);
action->set_localized_name(p_new_text);
action->set_edited(true);
}
}
void OpenXRActionEditor::_do_set_localized_name(const String p_new_text) {
action->set_localized_name(p_new_text);
action->set_edited(true);
action_localized_name->set_text(p_new_text);
}
void OpenXRActionEditor::_on_item_selected(int p_idx) {
ERR_FAIL_INDEX(p_idx, OpenXRAction::OPENXR_ACTION_MAX);
OpenXRAction::ActionType action_type = OpenXRAction::ActionType(p_idx);
if (action->get_action_type() != action_type) {
undo_redo->create_action(TTR("Change Action Type"));
undo_redo->add_do_method(this, "_do_set_action_type", action_type);
undo_redo->add_undo_method(this, "_do_set_action_type", action->get_action_type());
undo_redo->commit_action(false);
action->set_action_type(action_type);
action->set_edited(true);
}
}
void OpenXRActionEditor::_do_set_action_type(OpenXRAction::ActionType p_action_type) {
action->set_action_type(p_action_type);
action->set_edited(true);
action_type_button->select(int(action->get_action_type()));
}
void OpenXRActionEditor::_on_remove_action() {
emit_signal("remove", this);
}
OpenXRActionEditor::OpenXRActionEditor(Ref<OpenXRAction> p_action) {
undo_redo = EditorUndoRedoManager::get_singleton();
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_tooltip_text(TTR("Internal name of the action. Some XR runtimes don't allow spaces or special characters."));
action_name->set_custom_minimum_size(Size2(150.0 * EDSCALE, 0.0));
action_name->connect(SceneStringName(text_changed), callable_mp(this, &OpenXRActionEditor::_on_action_name_changed));
action_name->set_accessibility_name(TTRC("Action Name"));
add_child(action_name);
action_localized_name = memnew(LineEdit);
action_localized_name->set_text(action->get_localized_name());
action_localized_name->set_tooltip_text(TTR("Human-readable name of the action. This can be displayed to end users."));
action_localized_name->set_custom_minimum_size(Size2(150.0 * EDSCALE, 0.0));
action_localized_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_localized_name->connect(SceneStringName(text_changed), callable_mp(this, &OpenXRActionEditor::_on_action_localized_name_changed));
action_localized_name->set_accessibility_name(TTRC("Action Localized Name"));
add_child(action_localized_name);
action_type_button = memnew(OptionButton);
action_type_button->set_tooltip_text(TTR("Type of the action"));
action_type_button->add_item("Bool", OpenXRAction::OPENXR_ACTION_BOOL);
action_type_button->add_item("Float", OpenXRAction::OPENXR_ACTION_FLOAT);
action_type_button->add_item("Vector2", OpenXRAction::OPENXR_ACTION_VECTOR2);
action_type_button->add_item("Pose", OpenXRAction::OPENXR_ACTION_POSE);
action_type_button->add_item("Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC);
action_type_button->set_accessibility_name(TTRC("Action Type"));
action_type_button->select(int(action->get_action_type()));
action_type_button->set_custom_minimum_size(Size2(100.0 * EDSCALE, 0.0));
action_type_button->connect(SceneStringName(item_selected), callable_mp(this, &OpenXRActionEditor::_on_item_selected));
add_child(action_type_button);
// 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_text(TTR("Remove action"));
rem_action->connect(SceneStringName(pressed), callable_mp(this, &OpenXRActionEditor::_on_remove_action));
rem_action->set_flat(true);
add_child(rem_action);
}

View File

@@ -0,0 +1,72 @@
/**************************************************************************/
/* openxr_action_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action.h"
#include "editor/editor_undo_redo_manager.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:
EditorUndoRedoManager *undo_redo;
Ref<OpenXRAction> action;
LineEdit *action_name = nullptr;
LineEdit *action_localized_name = nullptr;
OptionButton *action_type_button = 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);
// used for undo/redo
void _do_set_name(const String p_new_text);
void _do_set_localized_name(const String p_new_text);
void _do_set_action_type(OpenXRAction::ActionType p_action_type);
public:
Ref<OpenXRAction> get_action() { return action; }
OpenXRActionEditor(Ref<OpenXRAction> p_action);
};

View File

@@ -0,0 +1,497 @@
/**************************************************************************/
/* openxr_action_map_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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_node.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/themes/editor_scale.h"
HashMap<String, String> OpenXRActionMapEditor::interaction_profile_editors;
HashMap<String, String> OpenXRActionMapEditor::binding_modifier_editors;
void OpenXRActionMapEditor::_bind_methods() {
ClassDB::bind_method("_add_action_set_editor", &OpenXRActionMapEditor::_add_action_set_editor);
ClassDB::bind_method("_add_interaction_profile_editor", &OpenXRActionMapEditor::_add_interaction_profile_editor);
ClassDB::bind_method(D_METHOD("_add_action_set", "name"), &OpenXRActionMapEditor::_add_action_set);
ClassDB::bind_method(D_METHOD("_remove_action_set", "name"), &OpenXRActionMapEditor::_remove_action_set);
ClassDB::bind_method(D_METHOD("_do_add_action_set_editor", "action_set_editor"), &OpenXRActionMapEditor::_do_add_action_set_editor);
ClassDB::bind_method(D_METHOD("_do_remove_action_set_editor", "action_set_editor"), &OpenXRActionMapEditor::_do_remove_action_set_editor);
ClassDB::bind_method(D_METHOD("_do_add_interaction_profile_editor", "interaction_profile_editor"), &OpenXRActionMapEditor::_do_add_interaction_profile_editor);
ClassDB::bind_method(D_METHOD("_do_remove_interaction_profile_editor", "interaction_profile_editor"), &OpenXRActionMapEditor::_do_remove_interaction_profile_editor);
ClassDB::bind_static_method("OpenXRActionMapEditor", D_METHOD("register_interaction_profile_editor", "interaction_profile_path", "editor_class"), &OpenXRActionMapEditor::register_interaction_profile_editor);
ClassDB::bind_static_method("OpenXRActionMapEditor", D_METHOD("register_binding_modifier_editor", "binding_modifier_class", "editor_class"), &OpenXRActionMapEditor::register_binding_modifier_editor);
}
void OpenXRActionMapEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
for (int i = 0; i < tabs->get_child_count(); i++) {
Control *tab = Object::cast_to<Control>(tabs->get_child(i));
if (tab) {
tab->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
}
}
} break;
case NOTIFICATION_READY: {
_create_action_sets();
_create_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::_create_action_sets() {
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 (interaction_profile_editors.has(profile_path)) {
Object *new_editor = ClassDB::instantiate(interaction_profile_editors[profile_path]);
if (new_editor) {
new_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(new_editor);
if (!new_profile_editor) {
WARN_PRINT("Interaction profile editor type mismatch for " + profile_path);
memfree(new_editor);
}
}
}
if (!new_profile_editor) {
// instance generic editor
new_profile_editor = memnew(OpenXRInteractionProfileEditor);
}
// now add it in..
ERR_FAIL_NULL_V(new_profile_editor, nullptr);
new_profile_editor->setup(action_map, p_interaction_profile);
tabs->add_child(new_profile_editor);
new_profile_editor->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
tabs->set_tab_button_icon(tabs->get_tab_count() - 1, get_theme_icon(SNAME("close"), SNAME("TabBar")));
if (!new_profile_editor->tooltip.is_empty()) {
tabs->set_tab_tooltip(tabs->get_tab_count() - 1, new_profile_editor->tooltip);
}
return new_profile_editor;
}
void OpenXRActionMapEditor::_create_interaction_profiles() {
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);
action_map->set_edited(true);
// update our editor right away
OpenXRActionSetEditor *action_set_editor = _add_action_set_editor(new_action_set);
undo_redo->create_action(TTR("Add action set"));
undo_redo->add_do_method(this, "_do_add_action_set_editor", action_set_editor);
undo_redo->add_undo_method(this, "_do_remove_action_set_editor", action_set_editor);
undo_redo->commit_action(false);
return action_set_editor;
}
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());
for (int i = 0; i < actionsets_vb->get_child_count(); i++) {
OpenXRActionSetEditor *action_set_editor = Object::cast_to<OpenXRActionSetEditor>(actionsets_vb->get_child(i));
if (action_set_editor && action_set_editor->get_action_set() == action_set) {
_on_remove_action_set(action_set_editor);
}
}
}
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);
callable_mp(this, &OpenXRActionMapEditor::_set_focus_on_action_set).call_deferred(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());
// Remove all actions first.
action_set_editor->remove_all_actions();
// Make sure we update our interaction profiles.
for (int i = 0; i < tabs->get_tab_count(); i++) {
// First tab won't be an interaction profile editor, but being thorough..
OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i));
if (interaction_profile_editor) {
interaction_profile_editor->remove_all_for_action_set(action_set);
}
}
// And now we can remove our action set.
undo_redo->create_action(TTR("Remove action set"));
undo_redo->add_do_method(this, "_do_remove_action_set_editor", action_set_editor);
undo_redo->add_undo_method(this, "_do_add_action_set_editor", action_set_editor);
undo_redo->commit_action(true);
action_map->set_edited(true);
}
void OpenXRActionMapEditor::_on_action_removed(Ref<OpenXRAction> p_action) {
for (int i = 0; i < tabs->get_tab_count(); i++) {
// First tab won't be an interaction profile editor, but being thorough..
OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i));
if (interaction_profile_editor) {
interaction_profile_editor->remove_all_for_action(p_action);
}
}
}
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);
action_map->set_edited(true);
OpenXRInteractionProfileEditorBase *interaction_profile_editor = _add_interaction_profile_editor(new_profile);
undo_redo->create_action(TTR("Add interaction profile"));
undo_redo->add_do_method(this, "_do_add_interaction_profile_editor", interaction_profile_editor);
undo_redo->add_undo_method(this, "_do_remove_interaction_profile_editor", interaction_profile_editor);
undo_redo->commit_action(false);
tabs->set_current_tab(tabs->get_tab_count() - 1);
}
void OpenXRActionMapEditor::_load_action_map(const String p_path, bool p_create_new_if_missing) {
Error err = OK;
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (da->file_exists(p_path)) {
action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
if (err != OK) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Error loading %s: %s."), edited_path, error_names[err]));
edited_path = "";
header_label->set_text("");
return;
}
} else if (p_create_new_if_missing) {
action_map.instantiate();
action_map->create_default_action_sets();
// Save it immediately
err = ResourceSaver::save(action_map, p_path);
if (err != OK) {
// Show warning but continue.
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), p_path, error_names[err]));
}
}
edited_path = p_path;
header_label->set_text(TTR("OpenXR Action map:") + " " + edited_path.get_file());
}
void OpenXRActionMapEditor::_on_save_action_map() {
Error err = ResourceSaver::save(action_map, edited_path);
if (err != OK) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), edited_path, error_names[err]));
return;
}
// TODO should clear undo/redo history
// out with the old
_clear_action_map();
_create_action_sets();
_create_interaction_profiles();
}
void OpenXRActionMapEditor::_on_reset_to_default_layout() {
// TODO should clear undo/redo history
// out with the old
_clear_action_map();
// create a new one
action_map.unref();
action_map.instantiate();
action_map->create_default_action_sets();
action_map->set_edited(true);
_create_action_sets();
_create_interaction_profiles();
}
void OpenXRActionMapEditor::_on_tabs_tab_changed(int p_tab) {
}
void OpenXRActionMapEditor::_on_tab_button_pressed(int p_tab) {
OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(p_tab));
ERR_FAIL_NULL(interaction_profile_editor);
undo_redo->create_action(TTR("Remove interaction profile"));
undo_redo->add_do_method(this, "_do_remove_interaction_profile_editor", interaction_profile_editor);
undo_redo->add_undo_method(this, "_do_add_interaction_profile_editor", interaction_profile_editor);
undo_redo->commit_action(true);
action_map->set_edited(true);
}
void OpenXRActionMapEditor::_do_add_action_set_editor(OpenXRActionSetEditor *p_action_set_editor) {
Ref<OpenXRActionSet> action_set = p_action_set_editor->get_action_set();
ERR_FAIL_COND(action_set.is_null());
action_map->add_action_set(action_set);
actionsets_vb->add_child(p_action_set_editor);
}
void OpenXRActionMapEditor::_do_remove_action_set_editor(OpenXRActionSetEditor *p_action_set_editor) {
Ref<OpenXRActionSet> action_set = p_action_set_editor->get_action_set();
ERR_FAIL_COND(action_set.is_null());
actionsets_vb->remove_child(p_action_set_editor);
action_map->remove_action_set(action_set);
}
void OpenXRActionMapEditor::_do_add_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor) {
Ref<OpenXRInteractionProfile> interaction_profile = p_interaction_profile_editor->get_interaction_profile();
ERR_FAIL_COND(interaction_profile.is_null());
action_map->add_interaction_profile(interaction_profile);
tabs->add_child(p_interaction_profile_editor);
tabs->set_tab_button_icon(tabs->get_tab_count() - 1, get_theme_icon(SNAME("close"), SNAME("TabBar")));
tabs->set_current_tab(tabs->get_tab_count() - 1);
}
void OpenXRActionMapEditor::_do_remove_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor) {
Ref<OpenXRInteractionProfile> interaction_profile = p_interaction_profile_editor->get_interaction_profile();
ERR_FAIL_COND(interaction_profile.is_null());
tabs->remove_child(p_interaction_profile_editor);
action_map->remove_interaction_profile(interaction_profile);
}
void OpenXRActionMapEditor::open_action_map(String p_path) {
EditorNode::get_bottom_panel()->make_item_visible(this);
// out with the old...
_clear_action_map();
// now load in our new action map
_load_action_map(p_path);
_create_action_sets();
_create_interaction_profiles();
}
void OpenXRActionMapEditor::_clear_action_map() {
while (actionsets_vb->get_child_count() > 0) {
Node *child = actionsets_vb->get_child(0);
actionsets_vb->remove_child(child);
child->queue_free();
}
for (int i = tabs->get_tab_count() - 1; i >= 0; --i) {
// First tab won't be an interaction profile editor, but being thorough..
OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i));
if (interaction_profile_editor) {
tabs->remove_child(interaction_profile_editor);
interaction_profile_editor->queue_free();
}
}
}
void OpenXRActionMapEditor::register_interaction_profile_editor(const String &p_for_path, const String &p_editor_class) {
interaction_profile_editors[p_for_path] = p_editor_class;
}
void OpenXRActionMapEditor::register_binding_modifier_editor(const String &p_binding_modifier_class, const String &p_editor_class) {
binding_modifier_editors[p_binding_modifier_class] = p_editor_class;
}
String OpenXRActionMapEditor::get_binding_modifier_editor_class(const String &p_binding_modifier_class) {
if (binding_modifier_editors.has(p_binding_modifier_class)) {
return binding_modifier_editors[p_binding_modifier_class];
}
return OpenXRBindingModifierEditor::get_class_static();
}
OpenXRActionMapEditor::OpenXRActionMapEditor() {
undo_redo = EditorUndoRedoManager::get_singleton();
set_custom_minimum_size(Size2(0.0, 300.0 * EDSCALE));
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_text(TTR("Add an action set."));
add_action_set->connect(SceneStringName(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_text(TTR("Add an interaction profile."));
add_interaction_profile->connect(SceneStringName(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_text(TTR("Save this OpenXR action map."));
save_as->connect(SceneStringName(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_text(TTR("Reset to default OpenXR action map."));
_default->connect(SceneStringName(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->set_theme_type_variation("TabContainerOdd");
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);
// Our Action map editor is only shown if openxr is enabled in project settings
// So load our action map and if it doesn't exist, create it right away.
_load_action_map(ResourceUID::ensure_path(GLOBAL_GET("xr/openxr/default_action_map")), true);
}

View File

@@ -0,0 +1,113 @@
/**************************************************************************/
/* openxr_action_map_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "openxr_action_set_editor.h"
#include "openxr_interaction_profile_editor.h"
#include "openxr_select_interaction_profile_dialog.h"
#include "core/templates/hash_map.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/plugins/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:
static HashMap<String, String> interaction_profile_editors; // interaction profile path, interaction profile editor
static HashMap<String, String> binding_modifier_editors; // binding modifier class, binding modifiers editor
EditorUndoRedoManager *undo_redo;
String edited_path;
Ref<OpenXRActionMap> action_map;
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 _create_action_sets();
OpenXRInteractionProfileEditorBase *_add_interaction_profile_editor(Ref<OpenXRInteractionProfile> p_interaction_profile);
void _create_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(Ref<OpenXRAction> p_action);
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);
void _clear_action_map();
// used for undo/redo
void _do_add_action_set_editor(OpenXRActionSetEditor *p_action_set_editor);
void _do_remove_action_set_editor(OpenXRActionSetEditor *p_action_set_editor);
void _do_add_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor);
void _do_remove_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor);
public:
static void register_interaction_profile_editor(const String &p_for_path, const String &p_editor_class);
static void register_binding_modifier_editor(const String &p_binding_modifier_class, const String &p_editor_class);
static String get_binding_modifier_editor_class(const String &p_binding_modifier_class);
void open_action_map(String p_path);
OpenXRActionMapEditor();
};

View File

@@ -0,0 +1,301 @@
/**************************************************************************/
/* openxr_action_set_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "editor/editor_string_names.h"
#include "editor/gui/editor_spin_slider.h"
#include "editor/themes/editor_scale.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"
void OpenXRActionSetEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_do_set_name", "name"), &OpenXRActionSetEditor::_do_set_name);
ClassDB::bind_method(D_METHOD("_do_set_localized_name", "name"), &OpenXRActionSetEditor::_do_set_localized_name);
ClassDB::bind_method(D_METHOD("_do_set_priority", "value"), &OpenXRActionSetEditor::_do_set_priority);
ClassDB::bind_method(D_METHOD("_do_add_action_editor", "action_editor"), &OpenXRActionSetEditor::_do_add_action_editor);
ClassDB::bind_method(D_METHOD("_do_remove_action_editor", "action_editor"), &OpenXRActionSetEditor::_do_remove_action_editor);
ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_set_editor")));
ADD_SIGNAL(MethodInfo("action_removed", PropertyInfo(Variant::OBJECT, "action")));
}
void OpenXRActionSetEditor::_set_fold_icon() {
if (is_expanded) {
fold_btn->set_button_icon(get_theme_icon(SNAME("GuiTreeArrowDown"), EditorStringName(EditorIcons)));
} else {
fold_btn->set_button_icon(get_theme_icon(SNAME("GuiTreeArrowRight"), EditorStringName(EditorIcons)));
}
}
void OpenXRActionSetEditor::_theme_changed() {
_set_fold_icon();
add_action->set_button_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
rem_action_set->set_button_icon(get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
}
void OpenXRActionSetEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
_theme_changed();
panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(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::_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) {
if (action_set->get_name() != p_new_text) {
undo_redo->create_action(TTR("Rename Action Set"));
undo_redo->add_do_method(this, "_do_set_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_name", action_set->get_name());
undo_redo->commit_action(false);
// If our localized name matches our action set name, set this too
if (action_set->get_name() == action_set->get_localized_name()) {
undo_redo->create_action(TTR("Rename Action Sets Localized name"));
undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_localized_name", action_set->get_localized_name());
undo_redo->commit_action(false);
action_set->set_localized_name(p_new_text);
action_set_localized_name->set_text(p_new_text);
}
action_set->set_name(p_new_text);
action_set->set_edited(true);
}
}
void OpenXRActionSetEditor::_do_set_name(const String p_new_text) {
action_set->set_name(p_new_text);
action_set_name->set_text(p_new_text);
}
void OpenXRActionSetEditor::_on_action_set_localized_name_changed(const String p_new_text) {
if (action_set->get_localized_name() != p_new_text) {
undo_redo->create_action(TTR("Rename Action Sets Localized name"));
undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
undo_redo->add_undo_method(this, "_do_set_localized_name", action_set->get_localized_name());
undo_redo->commit_action(false);
action_set->set_localized_name(p_new_text);
action_set->set_edited(true);
}
}
void OpenXRActionSetEditor::_do_set_localized_name(const String p_new_text) {
action_set->set_localized_name(p_new_text);
action_set_localized_name->set_text(p_new_text);
}
void OpenXRActionSetEditor::_on_action_set_priority_changed(const double p_new_value) {
int64_t value = (int64_t)p_new_value;
if (action_set->get_priority() != value) {
undo_redo->create_action(TTR("Change Action Sets priority"));
undo_redo->add_do_method(this, "_do_set_priority", value);
undo_redo->add_undo_method(this, "_do_set_priority", action_set->get_priority());
undo_redo->commit_action(false);
action_set->set_priority(value);
action_set->set_edited(true);
}
}
void OpenXRActionSetEditor::_do_set_priority(int64_t p_value) {
action_set->set_priority(p_value);
action_set_priority->set_value_no_signal(p_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);
action_set->set_edited(true);
OpenXRActionEditor *action_editor = _add_action_editor(new_action);
undo_redo->create_action(TTR("Add action"));
undo_redo->add_do_method(this, "_do_add_action_editor", action_editor);
undo_redo->add_undo_method(this, "_do_remove_action_editor", action_editor);
undo_redo->commit_action(false);
// 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());
emit_signal("action_removed", action);
undo_redo->create_action(TTR("Delete action"));
undo_redo->add_do_method(this, "_do_remove_action_editor", action_editor);
undo_redo->add_undo_method(this, "_do_add_action_editor", action_editor);
undo_redo->commit_action(true);
action_set->set_edited(true);
}
void OpenXRActionSetEditor::_do_add_action_editor(OpenXRActionEditor *p_action_editor) {
Ref<OpenXRAction> action = p_action_editor->get_action();
ERR_FAIL_COND(action.is_null());
action_set->add_action(action);
actions_vb->add_child(p_action_editor);
}
void OpenXRActionSetEditor::_do_remove_action_editor(OpenXRActionEditor *p_action_editor) {
Ref<OpenXRAction> action = p_action_editor->get_action();
ERR_FAIL_COND(action.is_null());
actions_vb->remove_child(p_action_editor);
action_set->remove_action(action);
}
void OpenXRActionSetEditor::remove_all_actions() {
for (int i = actions_vb->get_child_count(); i > 0; --i) {
_on_remove_action(actions_vb->get_child(i));
}
}
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) {
undo_redo = EditorUndoRedoManager::get_singleton();
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(SceneStringName(pressed), callable_mp(this, &OpenXRActionSetEditor::_on_toggle_expand));
fold_btn->set_accessibility_name(TTRC("Fold"));
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_tooltip_text(TTR("Internal name of the action. Some XR runtimes don't allow spaces or special characters."));
action_set_name->set_custom_minimum_size(Size2(150.0 * EDSCALE, 0.0));
action_set_name->connect(SceneStringName(text_changed), callable_mp(this, &OpenXRActionSetEditor::_on_action_set_name_changed));
action_set_name->set_accessibility_name(TTRC("Action Set Name"));
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_tooltip_text(TTR("Human-readable name of the action set. This can be displayed to end users."));
action_set_localized_name->set_custom_minimum_size(Size2(150.0 * EDSCALE, 0.0));
action_set_localized_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_set_localized_name->connect(SceneStringName(text_changed), callable_mp(this, &OpenXRActionSetEditor::_on_action_set_localized_name_changed));
action_set_localized_name->set_accessibility_name(TTRC("Action Set Localized Name"));
action_set_hb->add_child(action_set_localized_name);
action_set_priority = memnew(EditorSpinSlider);
action_set_priority->set_tooltip_text(TTR("Priority of the action set. If multiple action sets bind to the same input, the action set with the highest priority will be updated."));
action_set_priority->set_editing_integer(true);
action_set_priority->set_min(2147483647.0);
action_set_priority->set_min(-2147483648.0);
action_set_priority->set_value_no_signal(action_set->get_priority());
action_set_priority->set_custom_minimum_size(Size2(75.0 * EDSCALE, 0.0));
action_set_priority->connect(SceneStringName(value_changed), callable_mp(this, &OpenXRActionSetEditor::_on_action_set_priority_changed));
action_set_priority->set_accessibility_name(TTRC("Action Set Priority"));
action_set_hb->add_child(action_set_priority);
add_action = memnew(Button);
add_action->set_tooltip_text(TTR("Add action."));
add_action->connect(SceneStringName(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_text(TTR("Remove action set."));
rem_action_set->connect(SceneStringName(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);
// Add our existing actions
Array actions = action_set->get_actions();
for (int i = 0; i < actions.size(); i++) {
Ref<OpenXRAction> action = actions[i];
_add_action_editor(action);
}
}

View File

@@ -0,0 +1,96 @@
/**************************************************************************/
/* openxr_action_set_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_action_set.h"
#include "openxr_action_editor.h"
class EditorSpinSlider;
class BoxContainer;
class Button;
class LineEdit;
class PanelContainer;
class TextEdit;
class OpenXRActionSetEditor : public HBoxContainer {
GDCLASS(OpenXRActionSetEditor, HBoxContainer);
private:
EditorUndoRedoManager *undo_redo;
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;
EditorSpinSlider *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 _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 double p_new_value);
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);
// used for undo/redo
void _do_set_name(const String p_new_text);
void _do_set_localized_name(const String p_new_text);
void _do_set_priority(int64_t value);
void _do_add_action_editor(OpenXRActionEditor *p_action_editor);
void _do_remove_action_editor(OpenXRActionEditor *p_action_editor);
public:
Ref<OpenXRActionSet> get_action_set() { return action_set; }
void set_focus_on_entry();
void remove_all_actions();
OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set);
};

View File

@@ -0,0 +1,289 @@
/**************************************************************************/
/* openxr_binding_modifier_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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_binding_modifier_editor.h"
#include "editor/editor_string_names.h"
#include "scene/gui/option_button.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// EditorPropertyActionSet
void EditorPropertyActionSet::_set_read_only(bool p_read_only) {
options->set_disabled(p_read_only);
}
void EditorPropertyActionSet::_option_selected(int p_which) {
Ref<OpenXRActionSet> val = options->get_item_metadata(p_which);
emit_changed(get_edited_property(), val);
}
void EditorPropertyActionSet::update_property() {
Variant current = get_edited_property_value();
if (current.get_type() == Variant::NIL) {
options->select(-1);
return;
}
Ref<OpenXRActionSet> which = current;
for (int i = 0; i < options->get_item_count(); i++) {
if (which == (Ref<OpenXRActionSet>)options->get_item_metadata(i)) {
options->select(i);
return;
}
}
// Couldn't find it? deselect..
options->select(-1);
}
void EditorPropertyActionSet::setup(const Ref<OpenXRActionMap> &p_action_map) {
options->clear();
Array action_sets = p_action_map->get_action_sets();
for (Ref<OpenXRActionSet> action_set : action_sets) {
options->add_item(action_set->get_localized_name());
options->set_item_metadata(-1, action_set);
}
}
void EditorPropertyActionSet::set_option_button_clip(bool p_enable) {
options->set_clip_text(p_enable);
}
EditorPropertyActionSet::EditorPropertyActionSet() {
options = memnew(OptionButton);
options->set_clip_text(true);
options->set_flat(true);
options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_child(options);
add_focusable(options);
options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyActionSet::_option_selected));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// EditorPropertyBindingPath
void EditorPropertyBindingPath::_set_read_only(bool p_read_only) {
options->set_disabled(p_read_only);
}
void EditorPropertyBindingPath::_option_selected(int p_which) {
String val = options->get_item_metadata(p_which);
emit_changed(get_edited_property(), val);
}
void EditorPropertyBindingPath::update_property() {
Variant current = get_edited_property_value();
if (current.get_type() == Variant::NIL) {
options->select(-1);
return;
}
String which = current;
for (int i = 0; i < options->get_item_count(); i++) {
if (which == (String)options->get_item_metadata(i)) {
options->select(i);
return;
}
}
// Couldn't find it? deselect..
options->select(-1);
}
void EditorPropertyBindingPath::setup(const String &p_interaction_profile_path, Vector<OpenXRAction::ActionType> p_include_action_types) {
options->clear();
const OpenXRInteractionProfileMetadata::InteractionProfile *profile_def = OpenXRInteractionProfileMetadata::get_singleton()->get_profile(p_interaction_profile_path);
// Determine toplevel paths
Vector<String> top_level_paths;
for (const OpenXRInteractionProfileMetadata::IOPath &io_path : profile_def->io_paths) {
if (!top_level_paths.has(io_path.top_level_path)) {
top_level_paths.push_back(io_path.top_level_path);
}
}
for (const String &top_level_path : top_level_paths) {
String top_level_name = OpenXRInteractionProfileMetadata::get_singleton()->get_top_level_name(top_level_path);
for (const OpenXRInteractionProfileMetadata::IOPath &io_path : profile_def->io_paths) {
if (io_path.top_level_path == top_level_path && p_include_action_types.has(io_path.action_type)) {
options->add_item(top_level_name + "/" + io_path.display_name);
options->set_item_metadata(-1, io_path.openxr_path);
}
}
}
}
void EditorPropertyBindingPath::set_option_button_clip(bool p_enable) {
options->set_clip_text(p_enable);
}
EditorPropertyBindingPath::EditorPropertyBindingPath() {
options = memnew(OptionButton);
options->set_clip_text(true);
options->set_flat(true);
options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_child(options);
add_focusable(options);
options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyBindingPath::_option_selected));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRBindingModifierEditor
bool EditorInspectorPluginBindingModifier::can_handle(Object *p_object) {
Ref<OpenXRBindingModifier> binding_modifier(Object::cast_to<OpenXRBindingModifier>(p_object));
return binding_modifier.is_valid();
}
bool EditorInspectorPluginBindingModifier::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
Ref<OpenXRActionBindingModifier> action_binding_modifier(Object::cast_to<OpenXRActionBindingModifier>(p_object));
if (action_binding_modifier.is_valid()) {
if (p_type == Variant::OBJECT && p_hint == PROPERTY_HINT_RESOURCE_TYPE && p_hint_text == OpenXRActionSet::get_class_static()) {
OpenXRIPBinding *ip_binding = action_binding_modifier->get_ip_binding();
ERR_FAIL_NULL_V(ip_binding, false);
OpenXRActionMap *action_map = ip_binding->get_action_map();
ERR_FAIL_NULL_V(action_map, false);
EditorPropertyActionSet *action_set_property = memnew(EditorPropertyActionSet);
action_set_property->setup(action_map);
add_property_editor(p_path, action_set_property);
return true;
}
return false;
}
Ref<OpenXRIPBindingModifier> ip_binding_modifier(Object::cast_to<OpenXRIPBindingModifier>(p_object));
if (ip_binding_modifier.is_valid()) {
if (p_type == Variant::OBJECT && p_hint == PROPERTY_HINT_RESOURCE_TYPE && p_hint_text == OpenXRActionSet::get_class_static()) {
OpenXRInteractionProfile *interaction_profile = ip_binding_modifier->get_interaction_profile();
ERR_FAIL_NULL_V(interaction_profile, false);
OpenXRActionMap *action_map = interaction_profile->get_action_map();
ERR_FAIL_NULL_V(action_map, false);
EditorPropertyActionSet *action_set_property = memnew(EditorPropertyActionSet);
action_set_property->setup(action_map);
add_property_editor(p_path, action_set_property);
return true;
}
if (p_type == Variant::STRING && p_hint == PROPERTY_HINT_TYPE_STRING && p_hint_text == "binding_path") {
EditorPropertyBindingPath *binding_path_property = memnew(EditorPropertyBindingPath);
OpenXRInteractionProfile *interaction_profile = ip_binding_modifier->get_interaction_profile();
ERR_FAIL_NULL_V(interaction_profile, false);
Vector<OpenXRAction::ActionType> action_types;
action_types.push_back(OpenXRAction::OPENXR_ACTION_VECTOR2);
binding_path_property->setup(interaction_profile->get_interaction_profile_path(), action_types);
add_property_editor(p_path, binding_path_property);
return true;
}
return false;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRBindingModifierEditor
void OpenXRBindingModifierEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_binding_modifier"), &OpenXRBindingModifierEditor::get_binding_modifier);
ClassDB::bind_method(D_METHOD("setup", "action_map", "binding_modifier"), &OpenXRBindingModifierEditor::setup);
ADD_SIGNAL(MethodInfo("binding_modifier_removed", PropertyInfo(Variant::OBJECT, "binding_modifier_editor")));
}
void OpenXRBindingModifierEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
rem_binding_modifier_btn->set_button_icon(get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
} break;
}
}
void OpenXRBindingModifierEditor::_on_remove_binding_modifier() {
// Tell parent to remove us
emit_signal("binding_modifier_removed", this);
}
OpenXRBindingModifierEditor::OpenXRBindingModifierEditor() {
undo_redo = EditorUndoRedoManager::get_singleton();
set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_vb = memnew(VBoxContainer);
main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
add_child(main_vb);
header_hb = memnew(HBoxContainer);
header_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_vb->add_child(header_hb);
binding_modifier_title = memnew(Label);
binding_modifier_title->set_h_size_flags(Control::SIZE_EXPAND_FILL);
header_hb->add_child(binding_modifier_title);
rem_binding_modifier_btn = memnew(Button);
rem_binding_modifier_btn->set_tooltip_text(TTR("Remove this binding modifier."));
rem_binding_modifier_btn->connect(SceneStringName(pressed), callable_mp(this, &OpenXRBindingModifierEditor::_on_remove_binding_modifier));
rem_binding_modifier_btn->set_flat(true);
header_hb->add_child(rem_binding_modifier_btn);
editor_inspector = memnew(EditorInspector);
editor_inspector->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
editor_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
editor_inspector->set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_vb->add_child(editor_inspector);
}
void OpenXRBindingModifierEditor::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRBindingModifier> p_binding_modifier) {
ERR_FAIL_NULL(binding_modifier_title);
ERR_FAIL_NULL(editor_inspector);
action_map = p_action_map;
binding_modifier = p_binding_modifier;
if (p_binding_modifier.is_valid()) {
binding_modifier_title->set_text(p_binding_modifier->get_description());
editor_inspector->set_object_class(p_binding_modifier->get_class());
editor_inspector->edit(p_binding_modifier.ptr());
}
}

View File

@@ -0,0 +1,110 @@
/**************************************************************************/
/* openxr_binding_modifier_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_action_set.h"
#include "../action_map/openxr_binding_modifier.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/inspector/editor_inspector.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
#include "scene/gui/panel_container.h"
class EditorPropertyActionSet : public EditorProperty {
GDCLASS(EditorPropertyActionSet, EditorProperty);
OptionButton *options = nullptr;
void _option_selected(int p_which);
protected:
virtual void _set_read_only(bool p_read_only) override;
public:
void setup(const Ref<OpenXRActionMap> &p_action_map);
virtual void update_property() override;
void set_option_button_clip(bool p_enable);
EditorPropertyActionSet();
};
class EditorPropertyBindingPath : public EditorProperty {
GDCLASS(EditorPropertyBindingPath, EditorProperty);
OptionButton *options = nullptr;
void _option_selected(int p_which);
protected:
virtual void _set_read_only(bool p_read_only) override;
public:
void setup(const String &p_interaction_profile_path, Vector<OpenXRAction::ActionType> p_include_action_types);
virtual void update_property() override;
void set_option_button_clip(bool p_enable);
EditorPropertyBindingPath();
};
class EditorInspectorPluginBindingModifier : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginBindingModifier, EditorInspectorPlugin);
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) override;
};
class OpenXRBindingModifierEditor : public PanelContainer {
GDCLASS(OpenXRBindingModifierEditor, PanelContainer);
private:
HBoxContainer *header_hb = nullptr;
Label *binding_modifier_title = nullptr;
Button *rem_binding_modifier_btn = nullptr;
EditorInspector *editor_inspector = nullptr;
protected:
VBoxContainer *main_vb = nullptr;
EditorUndoRedoManager *undo_redo;
Ref<OpenXRBindingModifier> binding_modifier;
Ref<OpenXRActionMap> action_map;
static void _bind_methods();
void _notification(int p_what);
void _on_remove_binding_modifier();
public:
Ref<OpenXRBindingModifier> get_binding_modifier() const { return binding_modifier; }
virtual void setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRBindingModifier> p_binding_modifier);
OpenXRBindingModifierEditor();
};

View File

@@ -0,0 +1,260 @@
/**************************************************************************/
/* openxr_binding_modifiers_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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_binding_modifiers_dialog.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "openxr_action_map_editor.h"
#include "editor/themes/editor_scale.h"
void OpenXRBindingModifiersDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_do_add_binding_modifier_editor", "binding_modifier_editor"), &OpenXRBindingModifiersDialog::_do_add_binding_modifier_editor);
ClassDB::bind_method(D_METHOD("_do_remove_binding_modifier_editor", "binding_modifier_editor"), &OpenXRBindingModifiersDialog::_do_remove_binding_modifier_editor);
}
void OpenXRBindingModifiersDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
_create_binding_modifiers();
} break;
case NOTIFICATION_THEME_CHANGED: {
if (binding_modifier_sc) {
binding_modifier_sc->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
}
} break;
}
}
OpenXRBindingModifierEditor *OpenXRBindingModifiersDialog::_add_binding_modifier_editor(Ref<OpenXRBindingModifier> p_binding_modifier) {
ERR_FAIL_COND_V(p_binding_modifier.is_null(), nullptr);
String class_name = p_binding_modifier->get_class();
ERR_FAIL_COND_V(class_name.is_empty(), nullptr);
String editor_class = OpenXRActionMapEditor::get_binding_modifier_editor_class(class_name);
ERR_FAIL_COND_V(editor_class.is_empty(), nullptr);
OpenXRBindingModifierEditor *new_editor = nullptr;
Object *obj = ClassDB::instantiate(editor_class);
if (obj) {
new_editor = Object::cast_to<OpenXRBindingModifierEditor>(obj);
if (!new_editor) {
// Not of correct type?? Free it.
memfree(obj);
}
}
ERR_FAIL_NULL_V(new_editor, nullptr);
new_editor->setup(action_map, p_binding_modifier);
new_editor->connect("binding_modifier_removed", callable_mp(this, &OpenXRBindingModifiersDialog::_on_remove_binding_modifier));
binding_modifiers_vb->add_child(new_editor);
new_editor->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
return new_editor;
}
void OpenXRBindingModifiersDialog::_create_binding_modifiers() {
Array new_binding_modifiers;
if (ip_binding.is_valid()) {
new_binding_modifiers = ip_binding->get_binding_modifiers();
} else if (interaction_profile.is_valid()) {
new_binding_modifiers = interaction_profile->get_binding_modifiers();
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
for (int i = 0; i < new_binding_modifiers.size(); i++) {
Ref<OpenXRBindingModifier> binding_modifier = new_binding_modifiers[i];
_add_binding_modifier_editor(binding_modifier);
}
}
void OpenXRBindingModifiersDialog::_on_add_binding_modifier() {
create_dialog->popup_create(false);
}
void OpenXRBindingModifiersDialog::_on_remove_binding_modifier(Object *p_binding_modifier_editor) {
if (ip_binding.is_valid()) {
ip_binding->set_edited(true);
} else if (interaction_profile.is_valid()) {
interaction_profile->set_edited(true);
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
OpenXRBindingModifierEditor *binding_modifier_editor = Object::cast_to<OpenXRBindingModifierEditor>(p_binding_modifier_editor);
ERR_FAIL_NULL(binding_modifier_editor);
ERR_FAIL_COND(binding_modifier_editor->get_parent() != binding_modifiers_vb);
undo_redo->create_action(TTR("Remove binding modifier"));
undo_redo->add_do_method(this, "_do_remove_binding_modifier_editor", binding_modifier_editor);
undo_redo->add_undo_method(this, "_do_add_binding_modifier_editor", binding_modifier_editor);
undo_redo->commit_action(true);
}
void OpenXRBindingModifiersDialog::_on_dialog_created() {
// Instance new binding modifier object
Variant obj = create_dialog->instantiate_selected();
ERR_FAIL_COND(obj.get_type() != Variant::OBJECT);
Ref<OpenXRBindingModifier> new_binding_modifier = obj;
ERR_FAIL_COND(new_binding_modifier.is_null());
if (ip_binding.is_valid()) {
// Add it to our binding.
ip_binding->add_binding_modifier(new_binding_modifier);
ip_binding->set_edited(true);
} else if (interaction_profile.is_valid()) {
// Add it to our interaction profile.
interaction_profile->add_binding_modifier(new_binding_modifier);
interaction_profile->set_edited(true);
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
// Create our editor for this.
OpenXRBindingModifierEditor *binding_modifier_editor = _add_binding_modifier_editor(new_binding_modifier);
ERR_FAIL_NULL(binding_modifier_editor);
// Add undo/redo.
undo_redo->create_action(TTR("Add binding modifier"));
undo_redo->add_do_method(this, "_do_add_binding_modifier_editor", binding_modifier_editor);
undo_redo->add_undo_method(this, "_do_remove_binding_modifier_editor", binding_modifier_editor);
undo_redo->commit_action(false);
}
void OpenXRBindingModifiersDialog::_do_add_binding_modifier_editor(OpenXRBindingModifierEditor *p_binding_modifier_editor) {
Ref<OpenXRBindingModifier> binding_modifier = p_binding_modifier_editor->get_binding_modifier();
ERR_FAIL_COND(binding_modifier.is_null());
if (ip_binding.is_valid()) {
// Add it to our binding
ip_binding->add_binding_modifier(binding_modifier);
} else if (interaction_profile.is_valid()) {
// Add it to our interaction profile
interaction_profile->add_binding_modifier(binding_modifier);
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
binding_modifiers_vb->add_child(p_binding_modifier_editor);
}
void OpenXRBindingModifiersDialog::_do_remove_binding_modifier_editor(OpenXRBindingModifierEditor *p_binding_modifier_editor) {
Ref<OpenXRBindingModifier> binding_modifier = p_binding_modifier_editor->get_binding_modifier();
ERR_FAIL_COND(binding_modifier.is_null());
if (ip_binding.is_valid()) {
// Remove it from our binding.
ip_binding->remove_binding_modifier(binding_modifier);
} else if (interaction_profile.is_valid()) {
// Removed it to from interaction profile.
interaction_profile->remove_binding_modifier(binding_modifier);
} else {
ERR_FAIL_MSG("No binding nor interaction profile specified.");
}
binding_modifiers_vb->remove_child(p_binding_modifier_editor);
}
OpenXRBindingModifiersDialog::OpenXRBindingModifiersDialog() {
undo_redo = EditorUndoRedoManager::get_singleton();
set_transient(true);
binding_modifier_sc = memnew(ScrollContainer);
binding_modifier_sc->set_custom_minimum_size(Size2(350.0 * EDSCALE, 0.0));
binding_modifier_sc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
binding_modifier_sc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
binding_modifier_sc->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
add_child(binding_modifier_sc);
binding_modifiers_vb = memnew(VBoxContainer);
binding_modifiers_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
binding_modifier_sc->add_child(binding_modifiers_vb);
binding_warning_label = memnew(Label);
binding_warning_label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
binding_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
binding_warning_label->set_text(TTR("Note: modifiers will only be applied if supported on the host system."));
binding_modifiers_vb->add_child(binding_warning_label);
add_binding_modifier_btn = memnew(Button);
add_binding_modifier_btn->set_text(TTR("Add binding modifier"));
add_binding_modifier_btn->connect("pressed", callable_mp(this, &OpenXRBindingModifiersDialog::_on_add_binding_modifier));
binding_modifiers_vb->add_child(add_binding_modifier_btn);
// TODO may need to create our own dialog for this that can filter on binding modifiers recorded on interaction profiles or on individual bindings.
create_dialog = memnew(CreateDialog);
create_dialog->set_transient(true);
create_dialog->connect("create", callable_mp(this, &OpenXRBindingModifiersDialog::_on_dialog_created));
add_child(create_dialog);
}
void OpenXRBindingModifiersDialog::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile, Ref<OpenXRIPBinding> p_ip_binding) {
OpenXRInteractionProfileMetadata *meta_data = OpenXRInteractionProfileMetadata::get_singleton();
action_map = p_action_map;
interaction_profile = p_interaction_profile;
ip_binding = p_ip_binding;
String profile_path = interaction_profile->get_interaction_profile_path();
if (ip_binding.is_valid()) {
String action_name = "unset";
String path_name = "unset";
Ref<OpenXRAction> action = p_ip_binding->get_action();
if (action.is_valid()) {
action_name = action->get_name_with_set();
}
const OpenXRInteractionProfileMetadata::IOPath *io_path = meta_data->get_io_path(profile_path, p_ip_binding->get_binding_path());
if (io_path != nullptr) {
path_name = io_path->display_name;
}
create_dialog->set_base_type("OpenXRActionBindingModifier");
set_title(TTR("Binding modifiers for:") + " " + action_name + ": " + path_name);
} else if (interaction_profile.is_valid()) {
String profile_name = profile_path;
const OpenXRInteractionProfileMetadata::InteractionProfile *profile_def = meta_data->get_profile(profile_path);
if (profile_def != nullptr) {
profile_name = profile_def->display_name;
}
create_dialog->set_base_type("OpenXRIPBindingModifier");
set_title(TTR("Binding modifiers for:") + " " + profile_name);
}
}

View File

@@ -0,0 +1,78 @@
/**************************************************************************/
/* openxr_binding_modifiers_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_interaction_profile.h"
#include "../editor/openxr_binding_modifier_editor.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/create_dialog.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/scroll_container.h"
class OpenXRBindingModifiersDialog : public AcceptDialog {
GDCLASS(OpenXRBindingModifiersDialog, AcceptDialog);
private:
ScrollContainer *binding_modifier_sc = nullptr;
VBoxContainer *binding_modifiers_vb = nullptr;
Label *binding_warning_label = nullptr;
Button *add_binding_modifier_btn = nullptr;
CreateDialog *create_dialog = nullptr;
OpenXRBindingModifierEditor *_add_binding_modifier_editor(Ref<OpenXRBindingModifier> p_binding_modifier);
void _create_binding_modifiers();
void _on_add_binding_modifier();
void _on_remove_binding_modifier(Object *p_binding_modifier_editor);
void _on_dialog_created();
protected:
EditorUndoRedoManager *undo_redo;
Ref<OpenXRActionMap> action_map;
Ref<OpenXRInteractionProfile> interaction_profile;
Ref<OpenXRIPBinding> ip_binding;
static void _bind_methods();
void _notification(int p_what);
// used for undo/redo
void _do_add_binding_modifier_editor(OpenXRBindingModifierEditor *p_binding_modifier_editor);
void _do_remove_binding_modifier_editor(OpenXRBindingModifierEditor *p_binding_modifier_editor);
public:
OpenXRBindingModifiersDialog();
void setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile, Ref<OpenXRIPBinding> p_ip_binding = Ref<OpenXRIPBinding>());
};

View File

@@ -0,0 +1,66 @@
/**************************************************************************/
/* openxr_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/settings/editor_command_palette.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_bottom_panel()->add_item(TTRC("OpenXR Action Map"), action_map_editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_openxr_action_map_bottom_panel", TTRC("Toggle OpenXR Action Map Bottom Panel")));
binding_modifier_inspector_plugin = Ref<EditorInspectorPluginBindingModifier>(memnew(EditorInspectorPluginBindingModifier));
EditorInspector::add_inspector_plugin(binding_modifier_inspector_plugin);
#ifndef ANDROID_ENABLED
select_runtime = memnew(OpenXRSelectRuntime);
add_control_to_container(CONTAINER_TOOLBAR, select_runtime);
#endif
}

View File

@@ -0,0 +1,56 @@
/**************************************************************************/
/* openxr_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "openxr_action_map_editor.h"
#include "openxr_binding_modifier_editor.h"
#include "openxr_select_runtime.h"
#include "editor/plugins/editor_plugin.h"
class OpenXREditorPlugin : public EditorPlugin {
GDCLASS(OpenXREditorPlugin, EditorPlugin);
OpenXRActionMapEditor *action_map_editor = nullptr;
Ref<EditorInspectorPluginBindingModifier> binding_modifier_inspector_plugin = nullptr;
#ifndef ANDROID_ENABLED
OpenXRSelectRuntime *select_runtime = nullptr;
#endif
public:
virtual String get_plugin_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();
};

View File

@@ -0,0 +1,405 @@
/**************************************************************************/
/* openxr_interaction_profile_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "../openxr_api.h"
#include "editor/editor_string_names.h"
///////////////////////////////////////////////////////////////////////////
// Interaction profile editor base
void OpenXRInteractionProfileEditorBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("setup", "action_map", "interaction_profile"), &OpenXRInteractionProfileEditorBase::setup);
ClassDB::bind_method(D_METHOD("_add_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_add_binding);
ClassDB::bind_method(D_METHOD("_remove_binding", "action", "path"), &OpenXRInteractionProfileEditorBase::_remove_binding);
}
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::_do_update_interaction_profile() {
if (!is_dirty) {
is_dirty = true;
callable_mp(this, &OpenXRInteractionProfileEditorBase::_update_interaction_profile).call_deferred();
}
}
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->find_binding(action, p_path);
if (binding.is_null()) {
// create a new binding
binding.instantiate();
binding->set_action(action);
binding->set_binding_path(p_path);
// add it to our interaction profile
interaction_profile->add_binding(binding);
interaction_profile->set_edited(true);
binding->set_edited(true);
}
// Update our toplevel paths
action->set_toplevel_paths(action_map->get_top_level_paths(action));
_do_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->find_binding(action, p_path);
if (binding.is_valid()) {
interaction_profile->remove_binding(binding);
interaction_profile->set_edited(true);
// Update our toplevel paths
action->set_toplevel_paths(action_map->get_top_level_paths(action));
_do_update_interaction_profile();
}
}
void OpenXRInteractionProfileEditorBase::_update_interaction_profile() {
if (!is_dirty) {
// no need to update
return;
}
// Nothing to do here for now..
// and we've updated it...
is_dirty = false;
}
void OpenXRInteractionProfileEditorBase::_theme_changed() {
if (binding_modifiers_btn) {
binding_modifiers_btn->set_button_icon(get_theme_icon(SNAME("Modifiers"), EditorStringName(EditorIcons)));
}
}
void OpenXRInteractionProfileEditorBase::remove_all_for_action_set(Ref<OpenXRActionSet> p_action_set) {
// Note, don't need to remove bindings themselves as remove_all_for_action will be called for each before this is called.
// TODO update binding modifiers
}
void OpenXRInteractionProfileEditorBase::remove_all_for_action(Ref<OpenXRAction> p_action) {
Vector<Ref<OpenXRIPBinding>> bindings = interaction_profile->get_bindings_for_action(p_action);
if (bindings.size() > 0) {
String action_name = p_action->get_name_with_set();
// For our undo/redo we process all paths
undo_redo->create_action(TTR("Remove action from interaction profile"));
for (const Ref<OpenXRIPBinding> &binding : bindings) {
undo_redo->add_do_method(this, "_remove_binding", action_name, binding->get_binding_path());
undo_redo->add_undo_method(this, "_add_binding", action_name, binding->get_binding_path());
}
undo_redo->commit_action(false);
// But remove them all in one go so we're more efficient in updating our UI.
for (const Ref<OpenXRIPBinding> &binding : bindings) {
interaction_profile->remove_binding(binding);
}
interaction_profile->set_edited(true);
// Update our toplevel paths
p_action->set_toplevel_paths(action_map->get_top_level_paths(p_action));
_do_update_interaction_profile();
}
}
void OpenXRInteractionProfileEditorBase::_on_open_binding_modifiers() {
binding_modifiers_dialog->popup_centered(Size2i(500, 400));
}
OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase() {
undo_redo = EditorUndoRedoManager::get_singleton();
set_h_size_flags(SIZE_EXPAND_FILL);
set_v_size_flags(SIZE_EXPAND_FILL);
interaction_profile_sc = memnew(ScrollContainer);
interaction_profile_sc->set_h_size_flags(SIZE_EXPAND_FILL);
interaction_profile_sc->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(interaction_profile_sc);
binding_modifiers_dialog = memnew(OpenXRBindingModifiersDialog);
add_child(binding_modifiers_dialog);
toolbar_vb = memnew(VBoxContainer);
toolbar_vb->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(toolbar_vb);
binding_modifiers_btn = memnew(Button);
binding_modifiers_btn->set_tooltip_text(TTR("Edit binding modifiers"));
binding_modifiers_btn->connect("pressed", callable_mp(this, &OpenXRInteractionProfileEditorBase::_on_open_binding_modifiers));
// TODO show visual difference if there are binding modifiers for this interaction profile
toolbar_vb->add_child(binding_modifiers_btn);
}
void OpenXRInteractionProfileEditorBase::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
ERR_FAIL_NULL(binding_modifiers_dialog);
binding_modifiers_dialog->setup(p_action_map, 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 = OpenXRInteractionProfileMetadata::get_singleton()->get_profile(profile_path);
if (profile_def != nullptr) {
profile_name = profile_def->display_name;
if (!profile_def->openxr_extension_name.is_empty()) {
profile_name += "*";
tooltip = vformat(TTR("Note: This interaction profile requires extension %s support."), profile_def->openxr_extension_name);
}
}
set_name(profile_name);
// Make sure it is updated when it enters the tree...
is_dirty = true;
}
///////////////////////////////////////////////////////////////////////////
// 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::_on_action_selected(const String p_action) {
undo_redo->create_action(TTR("Add binding"));
undo_redo->add_do_method(this, "_add_binding", p_action, selecting_for_io_path);
undo_redo->add_undo_method(this, "_remove_binding", p_action, selecting_for_io_path);
undo_redo->commit_action(true);
selecting_for_io_path = "";
}
void OpenXRInteractionProfileEditor::_on_remove_pressed(const String p_action, const String p_for_io_path) {
undo_redo->create_action(TTR("Remove binding"));
undo_redo->add_do_method(this, "_remove_binding", p_action, p_for_io_path);
undo_redo->add_undo_method(this, "_add_binding", p_action, p_for_io_path);
undo_redo->commit_action(true);
}
void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetadata::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_focus_mode(FOCUS_ACCESSIBILITY);
if (p_io_path->openxr_extension_name.is_empty()) {
path_label->set_text(p_io_path->display_name);
} else {
path_label->set_text(p_io_path->display_name + "*");
path_hb->set_tooltip_text(vformat(TTR("Note: This binding path requires extension %s support."), p_io_path->openxr_extension_name));
}
path_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
path_hb->add_child(path_label);
Label *type_label = memnew(Label);
type_label->set_focus_mode(FOCUS_ACCESSIBILITY);
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_button_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
path_add->set_flat(true);
path_add->connect(SceneStringName(pressed), callable_mp(this, &OpenXRInteractionProfileEditor::select_action_for).bind(String(p_io_path->openxr_path)));
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 (Ref<OpenXRIPBinding> binding : bindings) {
if (binding->get_binding_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_focus_mode(FOCUS_ACCESSIBILITY);
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);
OpenXRBindingModifiersDialog *action_binding_modifiers_dialog = memnew(OpenXRBindingModifiersDialog);
action_binding_modifiers_dialog->setup(action_map, interaction_profile, binding);
action_hb->add_child(action_binding_modifiers_dialog);
Button *action_binding_modifiers_btn = memnew(Button);
action_binding_modifiers_btn->set_flat(true);
action_binding_modifiers_btn->set_button_icon(get_theme_icon(SNAME("Modifiers"), EditorStringName(EditorIcons)));
action_binding_modifiers_btn->connect(SceneStringName(pressed), callable_mp((Window *)action_binding_modifiers_dialog, &Window::popup_centered).bind(Size2i(500, 400)));
action_binding_modifiers_btn->set_accessibility_name(TTRC("Modifiers"));
// TODO change style of button if there are binding modifiers
action_hb->add_child(action_binding_modifiers_btn);
Button *action_rem = memnew(Button);
action_rem->set_flat(true);
action_rem->set_button_icon(get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
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)));
action_rem->set_accessibility_name(TTRC("Remove"));
action_hb->add_child(action_rem);
}
}
}
}
void OpenXRInteractionProfileEditor::_update_interaction_profile() {
ERR_FAIL_NULL(profile_def);
if (!is_dirty) {
// no need to update
return;
}
PackedStringArray requested_extensions = OpenXRAPI::get_all_requested_extensions();
// out with the old...
while (interaction_profile_hb->get_child_count() > 0) {
memdelete(interaction_profile_hb->get_child(0));
}
// in with the new...
// Determine toplevel paths
Vector<String> top_level_paths;
for (int i = 0; i < profile_def->io_paths.size(); i++) {
const OpenXRInteractionProfileMetadata::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);
interaction_profile_hb->add_child(panel);
panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainer")));
VBoxContainer *container = memnew(VBoxContainer);
panel->add_child(container);
Label *label = memnew(Label);
label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
label->set_text(OpenXRInteractionProfileMetadata::get_singleton()->get_top_level_name(top_level_paths[i]));
container->add_child(label);
for (int j = 0; j < profile_def->io_paths.size(); j++) {
const OpenXRInteractionProfileMetadata::IOPath *io_path = &profile_def->io_paths[j];
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))) {
_add_io_path(container, io_path);
}
}
}
OpenXRInteractionProfileEditorBase::_update_interaction_profile();
}
void OpenXRInteractionProfileEditor::_theme_changed() {
OpenXRInteractionProfileEditorBase::_theme_changed();
interaction_profile_sc->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
for (int i = 0; i < interaction_profile_hb->get_child_count(); i++) {
Control *panel = Object::cast_to<Control>(interaction_profile_hb->get_child(i));
if (panel) {
panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainer")));
}
}
}
OpenXRInteractionProfileEditor::OpenXRInteractionProfileEditor() {
interaction_profile_hb = memnew(HBoxContainer);
interaction_profile_sc->add_child(interaction_profile_hb);
}
void OpenXRInteractionProfileEditor::setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
OpenXRInteractionProfileEditorBase::setup(p_action_map, p_interaction_profile);
select_action_dialog = memnew(OpenXRSelectActionDialog(p_action_map));
select_action_dialog->connect("action_selected", callable_mp(this, &OpenXRInteractionProfileEditor::_on_action_selected));
add_child(select_action_dialog);
}

View File

@@ -0,0 +1,107 @@
/**************************************************************************/
/* openxr_interaction_profile_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_map.h"
#include "../action_map/openxr_interaction_profile.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "../editor/openxr_binding_modifiers_dialog.h"
#include "editor/editor_undo_redo_manager.h"
#include "openxr_select_action_dialog.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
class OpenXRInteractionProfileEditorBase : public HBoxContainer {
GDCLASS(OpenXRInteractionProfileEditorBase, HBoxContainer);
private:
OpenXRBindingModifiersDialog *binding_modifiers_dialog = nullptr;
VBoxContainer *toolbar_vb = nullptr;
Button *binding_modifiers_btn = nullptr;
void _on_open_binding_modifiers();
protected:
EditorUndoRedoManager *undo_redo;
Ref<OpenXRInteractionProfile> interaction_profile;
Ref<OpenXRActionMap> action_map;
ScrollContainer *interaction_profile_sc = nullptr;
bool is_dirty = false;
static void _bind_methods();
void _notification(int p_what);
const OpenXRInteractionProfileMetadata::InteractionProfile *profile_def = nullptr;
public:
String tooltip; // Tooltip text to show on tab
Ref<OpenXRInteractionProfile> get_interaction_profile() { return interaction_profile; }
virtual void _update_interaction_profile();
virtual void _theme_changed();
void _do_update_interaction_profile();
void _add_binding(const String p_action, const String p_path);
void _remove_binding(const String p_action, const String p_path);
void remove_all_for_action_set(Ref<OpenXRActionSet> p_action_set);
void remove_all_for_action(Ref<OpenXRAction> p_action);
virtual void setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile);
OpenXRInteractionProfileEditorBase();
};
class OpenXRInteractionProfileEditor : public OpenXRInteractionProfileEditorBase {
GDCLASS(OpenXRInteractionProfileEditor, OpenXRInteractionProfileEditorBase);
private:
String selecting_for_io_path;
HBoxContainer *interaction_profile_hb = nullptr;
OpenXRSelectActionDialog *select_action_dialog = nullptr;
void _add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetadata::IOPath *p_io_path);
public:
void select_action_for(const String p_io_path);
void _on_action_selected(const String p_action);
void _on_remove_pressed(const String p_action, const String p_for_io_path);
virtual void _update_interaction_profile() override;
virtual void _theme_changed() override;
virtual void setup(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) override;
OpenXRInteractionProfileEditor();
};

View File

@@ -0,0 +1,135 @@
/**************************************************************************/
/* openxr_select_action_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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/themes/editor_scale.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_THEME_CHANGED: {
scroll->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
} break;
}
}
void OpenXRSelectActionDialog::_on_select_action(const String p_action) {
if (selected_action != "") {
NodePath button_path = action_buttons[selected_action];
Button *button = Object::cast_to<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 = Object::cast_to<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();
// In with the new.
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_focus_mode(Control::FOCUS_ACCESSIBILITY);
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();
action_button->set_flat(true);
action_button->set_text(action->get_name() + ": " + action->get_localized_name());
action_button->connect(SceneStringName(pressed), callable_mp(this, &OpenXRSelectActionDialog::_on_select_action).bind(action_name));
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 * EDSCALE, 400.0 * EDSCALE));
add_child(scroll);
main_vb = memnew(VBoxContainer);
main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
scroll->add_child(main_vb);
}

View File

@@ -0,0 +1,65 @@
/**************************************************************************/
/* openxr_select_action_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#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);
};

View File

@@ -0,0 +1,131 @@
/**************************************************************************/
/* openxr_select_interaction_profile_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "../openxr_api.h"
#include "editor/themes/editor_scale.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_THEME_CHANGED: {
scroll->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), 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 = Object::cast_to<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 = Object::cast_to<Button>(get_node(button_path));
if (button != nullptr) {
button->set_flat(false);
}
}
}
void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_include) {
int available_count = 0;
OpenXRInteractionProfileMetadata *meta_data = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(meta_data);
// Out with the old.
while (main_vb->get_child_count() > 1) {
memdelete(main_vb->get_child(1));
}
PackedStringArray requested_extensions = OpenXRAPI::get_all_requested_extensions();
selected_interaction_profile = "";
ip_buttons.clear();
// In with the new.
PackedStringArray interaction_profiles = meta_data->get_interaction_profile_paths();
for (const String &path : interaction_profiles) {
const String extension = meta_data->get_interaction_profile_extension(path);
if (!p_do_not_include.has(path) && (extension.is_empty() || requested_extensions.has(extension))) {
Button *ip_button = memnew(Button);
ip_button->set_flat(true);
ip_button->set_text(meta_data->get_profile(path)->display_name);
ip_button->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
ip_button->connect(SceneStringName(pressed), callable_mp(this, &OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile).bind(path));
main_vb->add_child(ip_button);
ip_buttons[path] = ip_button->get_path();
available_count++;
}
}
all_selected->set_visible(available_count == 0);
get_cancel_button()->set_visible(available_count > 0);
popup_centered();
}
void OpenXRSelectInteractionProfileDialog::ok_pressed() {
if (selected_interaction_profile != "") {
emit_signal("interaction_profile_selected", selected_interaction_profile);
}
hide();
}
OpenXRSelectInteractionProfileDialog::OpenXRSelectInteractionProfileDialog() {
set_title(TTR("Select an interaction profile"));
scroll = memnew(ScrollContainer);
scroll->set_custom_minimum_size(Size2(600.0 * EDSCALE, 400.0 * EDSCALE));
add_child(scroll);
main_vb = memnew(VBoxContainer);
main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
scroll->add_child(main_vb);
all_selected = memnew(Label);
all_selected->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
all_selected->set_text(TTR("All interaction profiles have been added to the action map."));
main_vb->add_child(all_selected);
}

View File

@@ -0,0 +1,59 @@
/**************************************************************************/
/* openxr_select_interaction_profile_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "scene/gui/box_container.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
#include "scene/gui/scroll_container.h"
class OpenXRSelectInteractionProfileDialog : public ConfirmationDialog {
GDCLASS(OpenXRSelectInteractionProfileDialog, ConfirmationDialog);
private:
String selected_interaction_profile;
Dictionary ip_buttons;
VBoxContainer *main_vb = nullptr;
ScrollContainer *scroll = nullptr;
Label *all_selected = 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();
};

View File

@@ -0,0 +1,128 @@
/**************************************************************************/
/* openxr_select_runtime.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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_runtime.h"
#include "core/io/dir_access.h"
#include "core/os/os.h"
#include "editor/settings/editor_settings.h"
void OpenXRSelectRuntime::_update_items() {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
OS *os = OS::get_singleton();
Dictionary runtimes = EDITOR_GET("xr/openxr/runtime_paths");
int current_runtime = 0;
int index = 0;
String current_path = os->get_environment("XR_RUNTIME_JSON");
// Parse the user's home folder.
String home_folder = os->get_environment("HOME");
if (home_folder.is_empty()) {
home_folder = os->get_environment("HOMEDRIVE") + os->get_environment("HOMEPATH");
}
clear();
add_item("Default", -1);
set_item_metadata(index, "");
index++;
for (const KeyValue<Variant, Variant> &kv : runtimes) {
const String &key = kv.key;
const String &path = kv.value;
String adj_path = path.replace("~", home_folder);
if (da->file_exists(adj_path)) {
add_item(key, index);
set_item_metadata(index, adj_path);
if (current_path == adj_path) {
current_runtime = index;
}
index++;
}
}
select(current_runtime);
}
void OpenXRSelectRuntime::_on_item_selected(int p_which) {
OS *os = OS::get_singleton();
if (p_which == 0) {
// Return to default runtime
os->set_environment("XR_RUNTIME_JSON", "");
} else {
// Select the runtime we want
String runtime_path = get_item_metadata(p_which);
os->set_environment("XR_RUNTIME_JSON", runtime_path);
}
}
void OpenXRSelectRuntime::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_ENTER_TREE: {
// Update dropdown
_update_items();
// Connect signal
connect(SceneStringName(item_selected), callable_mp(this, &OpenXRSelectRuntime::_on_item_selected));
} break;
case NOTIFICATION_EXIT_TREE: {
// Disconnect signal
disconnect(SceneStringName(item_selected), callable_mp(this, &OpenXRSelectRuntime::_on_item_selected));
} break;
}
}
OpenXRSelectRuntime::OpenXRSelectRuntime() {
Dictionary default_runtimes;
// Add known common runtimes by default.
#ifdef WINDOWS_ENABLED
default_runtimes["Meta"] = "C:\\Program Files\\Oculus\\Support\\oculus-runtime\\oculus_openxr_64.json";
default_runtimes["SteamVR"] = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\SteamVR\\steamxr_win64.json";
default_runtimes["Varjo"] = "C:\\Program Files\\Varjo\\varjo-openxr\\VarjoOpenXR.json";
default_runtimes["WMR"] = "C:\\WINDOWS\\system32\\MixedRealityRuntime.json";
#endif
#ifdef LINUXBSD_ENABLED
default_runtimes["Monado"] = "/usr/share/openxr/1/openxr_monado.json";
default_runtimes["SteamVR"] = "~/.steam/steam/steamapps/common/SteamVR/steamxr_linux64.json";
#endif
// TODO: Move to editor_settings.cpp
EDITOR_DEF_RST("xr/openxr/runtime_paths", default_runtimes);
set_flat(true);
set_theme_type_variation("TopBarOptionButton");
set_fit_to_longest_item(false);
set_focus_mode(Control::FOCUS_NONE);
set_tooltip_text(TTR("Choose an XR runtime."));
}

View File

@@ -0,0 +1,47 @@
/**************************************************************************/
/* openxr_select_runtime.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "scene/gui/option_button.h"
class OpenXRSelectRuntime : public OptionButton {
GDCLASS(OpenXRSelectRuntime, OptionButton);
public:
OpenXRSelectRuntime();
protected:
void _notification(int p_notification);
private:
void _update_items();
void _on_item_selected(int p_which);
};