Basic gamepad input

This commit is contained in:
rock88 2020-05-28 22:11:55 +03:00
parent fe73134192
commit 4aeabcf113
6 changed files with 184 additions and 5 deletions

View file

@ -22,6 +22,7 @@ struct MouseState {
struct MouseState mouse_state; struct MouseState mouse_state;
struct GamePadState game_pad_state; struct GamePadState game_pad_state;
static GLFWgamepadstate last_glfw_gamepad_state;
static int m_width = 1, m_height = 1; static int m_width = 1, m_height = 1;
@ -130,6 +131,30 @@ void InputController::handle_gamepad_event(GLFWgamepadstate* gamepad) {
SET_GAME_PAD_STATE(X_FLAG, GLFW_GAMEPAD_BUTTON_X); SET_GAME_PAD_STATE(X_FLAG, GLFW_GAMEPAD_BUTTON_X);
SET_GAME_PAD_STATE(Y_FLAG, GLFW_GAMEPAD_BUTTON_Y); SET_GAME_PAD_STATE(Y_FLAG, GLFW_GAMEPAD_BUTTON_Y);
} }
// Send to nanogui
if (gamepad->buttons != last_glfw_gamepad_state.buttons) {
for (int j = 0; j <= GLFW_GAMEPAD_BUTTON_LAST; j++) {
if (gamepad->buttons[j] == last_glfw_gamepad_state.buttons[j]) {
continue;
}
nanogui::gamepad_button_callback_event(0, j, gamepad->buttons[j]);
}
}
if (gamepad->axes != last_glfw_gamepad_state.axes) {
for (int j = 0; j <= GLFW_GAMEPAD_AXIS_LAST; j++) {
if (gamepad->axes[j] == last_glfw_gamepad_state.axes[j]) {
continue;
}
nanogui::gamepad_analog_callback_event(0, j, gamepad->axes[j]);
}
}
last_glfw_gamepad_state = *gamepad;
} }
void InputController::handle_rumple(unsigned short low_freq_motor, unsigned short high_freq_motor) { void InputController::handle_rumple(unsigned short low_freq_motor, unsigned short high_freq_motor) {

View file

@ -62,9 +62,21 @@ void Application::pop_window() {
m_windows.back()->set_visible(true); m_windows.back()->set_visible(true);
update_focus(m_windows.back()); update_focus(m_windows.back());
perform_layout(); perform_layout();
}
if (auto w = static_cast<ContentWindow *>(m_windows.front())) { if (auto w = static_cast<ContentWindow *>(m_windows.front())) {
w->window_appear(); w->window_appear();
}
}
}
void Application::gamepad_button_callback_event(int jid, int button, int action) {
if (!m_windows.empty()) {
m_windows.back()->gamepad_button_event(jid, button, action);
}
}
void Application::gamepad_analog_callback_event(int jid, int axis, float value) {
if (!m_windows.empty()) {
m_windows.back()->gamepad_analog_event(jid, axis, value);
} }
} }

View file

@ -18,6 +18,9 @@ public:
void pop_window(); void pop_window();
void gamepad_button_callback_event(int jid, int button, int action) override;
void gamepad_analog_callback_event(int jid, int axis, float value) override;
private: private:
std::vector<Widget *> m_windows; std::vector<Widget *> m_windows;
}; };

View file

@ -1,6 +1,8 @@
#include "ContentWindow.hpp" #include "ContentWindow.hpp"
#include "Application.hpp" #include "Application.hpp"
#include "LoadingOverlay.hpp"
#include "nanovg.h" #include "nanovg.h"
#include <nanogui/opengl.h>
using namespace nanogui; using namespace nanogui;
@ -74,3 +76,138 @@ void ContentWindow::set_right_title_button(int icon, const std::function<void()>
}); });
perform_layout(); perform_layout();
} }
static inline std::vector<Widget *> selectables_child_recursive(Widget *widget) {
std::vector<Widget *> selectables;
for (auto child: widget->children()) {
if (child->selectable()) {
selectables.push_back(child);
}
auto child_selectables = selectables_child_recursive(child);
selectables.insert(selectables.end(), child_selectables.begin(), child_selectables.end());
}
return selectables;
}
static inline Widget *most_closed_widget(Widget *target, std::vector<Widget *> widgets, bool left, bool right, bool up, bool down) {
if (widgets.size() < 2) {
return nullptr;
}
int index = -1;
int distanse = std::numeric_limits<int>::max();
for (int i = 0; i < widgets.size(); i++) {
auto widget = widgets[i];
if (widget == target) {
continue;
}
if (left && widget->center().y() == target->center().y() && widget->center().x() < target->center().x()) {
int new_distanse = target->center().x() - widget->center().x();
if (new_distanse < distanse) {
distanse = new_distanse;
index = i;
}
} else if (right && widget->center().y() == target->center().y() && widget->center().x() > target->center().x()) {
int new_distanse = widget->center().x() - target->center().x();
if (new_distanse < distanse) {
distanse = new_distanse;
index = i;
}
} else if (up && widget->center().x() == target->center().x() && widget->center().y() < target->center().y()) {
int new_distanse = target->center().y() - widget->center().y();
if (new_distanse < distanse) {
distanse = new_distanse;
index = i;
}
} else if (down && widget->center().x() == target->center().x() && widget->center().y() > target->center().y()) {
int new_distanse = widget->center().y() - target->center().y();
if (new_distanse < distanse) {
distanse = new_distanse;
index = i;
}
}
}
if (index != -1) {
return widgets[index];
}
return nullptr;
}
bool ContentWindow::gamepad_button_event(int jid, int button, int action) {
if (!action) {
return false;
}
std::vector<MessageDialog *> messages;
std::vector<LoadingOverlay *> loaders;
for (auto child: screen()->children()) {
if (auto message = dynamic_cast<MessageDialog *>(child)) {
messages.push_back(message);
}
if (auto loader = dynamic_cast<LoadingOverlay *>(child)) {
loaders.push_back(loader);
}
}
bool handle_button = messages.empty() && loaders.empty();
if (button == NANOGUI_GAMEPAD_BUTTON_B) {
if (!messages.empty()) {
for (auto message: messages) {
message->dispose();
}
return false;
}
if (!loaders.empty()) {
return false;
}
pop();
return false;
} else if (handle_button && button == NANOGUI_GAMEPAD_BUTTON_A) {
auto selectables = selectables_child_recursive(m_container);
auto current = std::find_if(selectables.begin(), selectables.end(), [](auto c) { return c->selected(); });
if (current != selectables.end()) {
auto widget = *current;
widget->mouse_button_event(widget->position() + widget->size() / 2, NANOGUI_MOUSE_BUTTON_1, 1, 0);
widget->mouse_button_event(widget->position() + widget->size() / 2, NANOGUI_MOUSE_BUTTON_1, 0, 0);
}
return false;
}
auto selectables = selectables_child_recursive(m_container);
auto current = std::find_if(selectables.begin(), selectables.end(), [](auto c) { return c->selected(); });
if (handle_button && button >= NANOGUI_GAMEPAD_BUTTON_DPAD_UP && button <= NANOGUI_GAMEPAD_BUTTON_DPAD_LEFT) {
if (current == selectables.end()) {
selectables.front()->set_selected(true);
return false;
}
auto current_selectable = *current;
auto new_selectable = most_closed_widget(
current_selectable,
selectables,
button == NANOGUI_GAMEPAD_BUTTON_DPAD_LEFT,
button == NANOGUI_GAMEPAD_BUTTON_DPAD_RIGHT,
button == NANOGUI_GAMEPAD_BUTTON_DPAD_UP,
button == NANOGUI_GAMEPAD_BUTTON_DPAD_DOWN
);
if (new_selectable) {
current_selectable->set_selected(false);
new_selectable->set_selected(true);
m_scroll->set_scroll((new_selectable->position().y() + new_selectable->height()) / m_scroll->height());
}
}
return false;
}

View file

@ -53,6 +53,8 @@ public:
return true; return true;
}; };
bool gamepad_button_event(int jid, int button, int action) override;
private: private:
Application* application() { Application* application() {
auto application = static_cast<Application *>(screen()); auto application = static_cast<Application *>(screen());

2
third_party/nanogui vendored

@ -1 +1 @@
Subproject commit ec772a923595c1d4b08156fcbf8f4631871dd642 Subproject commit 45d1e7539dcade92040ec406f063c73b3cdfae13