mirror of
https://github.com/Pixelplanet/Pixel-VR-Player.git
synced 2026-10-29 04:02:28 +01:00
fix(input): optimize keyboard height/tilt, isolate text fields, and fix transport dock trigger
This commit is contained in:
1 parent
f26405797e
commit
ec9821f1af
4 files changed
+80
-46
No files matched your search
+24
-3
@@ -313,6 +313,13 @@ void App::loadMedia(const std::string& path) {
|
||||
}
|
||||
hudDismissed_ = false;
|
||||
lastControllerActivity_ = std::chrono::steady_clock::now();
|
||||
VrKeyboard::instance().hide();
|
||||
actionMenu_.visible = false;
|
||||
smbModal_.active = false;
|
||||
smbModal_.editingField = SmbModalState::Field::None;
|
||||
browser_.setOpen(false);
|
||||
downloadsWindow_ = false;
|
||||
pendingDl_.clear();
|
||||
|
||||
// Refine the stereo layout from the frame aspect ratio when the filename did
|
||||
// not state it: once the projection is a VR type the aspect is unambiguous
|
||||
@@ -665,6 +672,8 @@ void App::saveSmbModal() {
|
||||
SmbCredentialsManager::instance().addOrUpdateShare(s);
|
||||
|
||||
smbModal_.active = false;
|
||||
smbModal_.editingField = SmbModalState::Field::None;
|
||||
VrKeyboard::instance().hide();
|
||||
actionMenu_.visible = false;
|
||||
browser_.openNetwork();
|
||||
#endif
|
||||
@@ -676,6 +685,8 @@ void App::deleteSmbModal() {
|
||||
SmbCredentialsManager::instance().deleteShare(smbModal_.origHost, smbModal_.origShare);
|
||||
}
|
||||
smbModal_.active = false;
|
||||
smbModal_.editingField = SmbModalState::Field::None;
|
||||
VrKeyboard::instance().hide();
|
||||
actionMenu_.visible = false;
|
||||
browser_.openNetwork();
|
||||
#endif
|
||||
@@ -1115,12 +1126,18 @@ int App::run() {
|
||||
if (in.browserToggle) {
|
||||
if (actionMenu_.visible) {
|
||||
actionMenu_.visible = false;
|
||||
smbModal_.active = false;
|
||||
smbModal_.editingField = SmbModalState::Field::None;
|
||||
VrKeyboard::instance().hide();
|
||||
} else if (downloadsWindow_) {
|
||||
downloadsWindow_ = false;
|
||||
} else if (settings_.isOpen()) {
|
||||
settings_.close();
|
||||
} else if (browser_.isOpen()) {
|
||||
browser_.setOpen(false);
|
||||
smbModal_.active = false;
|
||||
smbModal_.editingField = SmbModalState::Field::None;
|
||||
VrKeyboard::instance().hide();
|
||||
} else if (mediaOpen_ && renderer_.controlBarVisible()) {
|
||||
// HUD is visible during playback: B/Y dismisses the HUD!
|
||||
dismissHud(in, nowT);
|
||||
@@ -1544,11 +1561,11 @@ int App::run() {
|
||||
}
|
||||
}
|
||||
const bool uiPanelActive = browserOpen || settingsOpen ||
|
||||
downloadsWindow_ || !pendingDl_.empty() || actionMenu_.visible;
|
||||
downloadsWindow_ || !pendingDl_.empty() || actionMenu_.visible || smbModal_.active;
|
||||
// The Transport HUD is only visible during playback when the
|
||||
// Unified Spatial Hub and modals are closed.
|
||||
const bool barBlocked = browserOpen || settingsOpen || downloadsWindow_ ||
|
||||
!pendingDl_.empty() || actionMenu_.visible;
|
||||
!pendingDl_.empty() || actionMenu_.visible || smbModal_.active;
|
||||
// Controls (buttons + timeline) show only while a controller is
|
||||
// actively moving; they fade after a few seconds of stillness.
|
||||
renderer_.setControlBar(!barBlocked && controllersEngaged && !hudDismissed_ &&
|
||||
@@ -1932,6 +1949,8 @@ int App::run() {
|
||||
// Cancel
|
||||
actionMenu_.visible = false;
|
||||
smbModal_.active = false;
|
||||
smbModal_.editingField = SmbModalState::Field::None;
|
||||
VrKeyboard::instance().hide();
|
||||
} else {
|
||||
// Delete Share
|
||||
deleteSmbModal();
|
||||
@@ -1940,6 +1959,8 @@ int App::run() {
|
||||
// Cancel
|
||||
actionMenu_.visible = false;
|
||||
smbModal_.active = false;
|
||||
smbModal_.editingField = SmbModalState::Field::None;
|
||||
VrKeyboard::instance().hide();
|
||||
}
|
||||
}
|
||||
interacted = true;
|
||||
@@ -2209,7 +2230,7 @@ int App::run() {
|
||||
interacted = true;
|
||||
}
|
||||
// Click outside the control dock in empty space immediately hides the HUD
|
||||
if (in.select && btn < 0 && !renderer_.pointerOnTimeline() && !renderer_.pointerOnTimeToggle()) {
|
||||
if (in.select && !renderer_.pointerActive()) {
|
||||
dismissHud(in, nowT);
|
||||
interacted = true;
|
||||
}
|
||||
|
||||
+14
-13
@@ -1312,16 +1312,7 @@ void VulkanRenderer::updatePointer() {
|
||||
|
||||
// 0. Virtual Keyboard takes top focus priority
|
||||
if (kbOpen) {
|
||||
// First check if pointing at the text preview box on the panel
|
||||
if (hx >= -kPanelW && hx <= kPanelW && hyy >= -kPanelTop && hyy <= kPanelTop) {
|
||||
pointerActive_ = true;
|
||||
pointerHand_ = h;
|
||||
pointerHit_[0] = world.x;
|
||||
pointerHit_[1] = world.y;
|
||||
pointerHit_[2] = world.z;
|
||||
break;
|
||||
}
|
||||
// Next check if pointing directly at the virtual keyboard overlay keys
|
||||
// First priority: Check if pointing directly at the virtual keyboard overlay keys
|
||||
if (keyboardModal_.hasTransform) {
|
||||
const auto& m = keyboardModal_.transform;
|
||||
// Column 0: Local X axis (right)
|
||||
@@ -1341,9 +1332,9 @@ void VulkanRenderer::updatePointer() {
|
||||
const Vec3 rel = hitP - kbCenter;
|
||||
const float localX = dot(rel, kbRight);
|
||||
const float localY = dot(rel, kbUp);
|
||||
// Overlay width is 1.05m (halfW = 0.525m). Height is ~0.45m (halfH = 0.225m).
|
||||
// Allow generous interactive margin (0.58m x 0.28m)
|
||||
if (std::fabs(localX) <= 0.58f && std::fabs(localY) <= 0.28f) {
|
||||
// Overlay width is 1.05m (halfW = 0.525m). Height is ~0.40m (halfH = 0.20m).
|
||||
// Allow generous interactive margin (0.58m x 0.26m)
|
||||
if (std::fabs(localX) <= 0.58f && std::fabs(localY) <= 0.26f) {
|
||||
pointerActive_ = true;
|
||||
pointerHand_ = h;
|
||||
pointerHit_[0] = hitP.x;
|
||||
@@ -1354,6 +1345,16 @@ void VulkanRenderer::updatePointer() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second priority: Check if pointing at the floating modal text box directly above the keyboard
|
||||
// Modal box center is cy = 0.16f, half-width 0.23f, half-height 0.13f
|
||||
if (std::fabs(hx) <= 0.25f && std::fabs(hyy - 0.16f) <= 0.14f) {
|
||||
pointerActive_ = true;
|
||||
pointerHand_ = h;
|
||||
pointerHit_[0] = world.x;
|
||||
pointerHit_[1] = world.y;
|
||||
pointerHit_[2] = world.z;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+41
-30
@@ -212,11 +212,18 @@ bool VrKeyboard::show(const std::string& description,
|
||||
auto* ov = reinterpret_cast<vr::IVROverlay*>(vrOverlay_);
|
||||
if (!ov) return false;
|
||||
|
||||
if (isOpen_) {
|
||||
hide();
|
||||
}
|
||||
// Explicitly hide any existing keyboard session in SteamVR to clear lingering text buffers
|
||||
ov->HideKeyboard();
|
||||
|
||||
description_ = description;
|
||||
existingText_ = existingText;
|
||||
currentText_ = existingText;
|
||||
isPassword_ = isPassword;
|
||||
doneSubmitted_ = false;
|
||||
userTyped_ = false;
|
||||
|
||||
const vr::EGamepadTextInputMode mode = isPassword
|
||||
? vr::k_EGamepadTextInputModePassword
|
||||
@@ -257,15 +264,18 @@ bool VrKeyboard::show(const std::string& description,
|
||||
}
|
||||
const float yaw = std::atan2(-fx, -fz);
|
||||
|
||||
// Place keyboard 1.05m in front of eyes, 0.35m below eye level
|
||||
// Place keyboard 1.05m in front of eyes, 0.20m below eye level.
|
||||
// The modal dialog box above ends at ~hy + 0.03m; this places the top edge of the
|
||||
// keyboard (half-height ~0.20m) right at hy - 0.004m, exactly 3.4cm below the modal dialog.
|
||||
const float dist = 1.05f;
|
||||
const float kbX = hx + fx * dist;
|
||||
const float kbY = hy - 0.35f;
|
||||
const float kbY = hy - 0.20f;
|
||||
const float kbZ = hz + fz * dist;
|
||||
|
||||
// Tilt upward toward eyes by ~22 degrees (-0.38 rad).
|
||||
// Top side slants backward away from user; face normal points up and toward eyes.
|
||||
const float tilt = -0.38f;
|
||||
// Tilt upward toward eyes by ~11.5 degrees (-0.20 rad).
|
||||
// Center is 20cm below eyes at 1.05m distance: atan2(0.20, 1.05) ~= 0.189 rad.
|
||||
// Tilt of -0.20 rad angles the keyboard face normal directly at the user's eyes!
|
||||
const float tilt = -0.20f;
|
||||
vr::HmdMatrix34_t kbMat{};
|
||||
kbMat.m[0][0] = std::cos(yaw);
|
||||
kbMat.m[0][1] = std::sin(yaw) * std::sin(tilt);
|
||||
@@ -329,6 +339,9 @@ void VrKeyboard::hide() {
|
||||
isOpen_ = false;
|
||||
kbConfigured_ = false;
|
||||
doneSubmitted_ = false;
|
||||
userTyped_ = false;
|
||||
currentText_.clear();
|
||||
existingText_.clear();
|
||||
PIXELVR_LOG_INFO("VrKeyboard: Hidden virtual keyboard");
|
||||
}
|
||||
|
||||
@@ -342,6 +355,7 @@ void VrKeyboard::poll(std::function<void(const std::string&)> onDone,
|
||||
vr::VREvent_t event{};
|
||||
while (sys->PollNextEvent(&event, sizeof(event))) {
|
||||
if (event.eventType == vr::VREvent_KeyboardCharInput) {
|
||||
userTyped_ = true;
|
||||
char keyBuf[1024] = {0};
|
||||
uint32_t len = ov->GetKeyboardText(keyBuf, sizeof(keyBuf));
|
||||
if (len > 0) {
|
||||
@@ -374,6 +388,10 @@ void VrKeyboard::poll(std::function<void(const std::string&)> onDone,
|
||||
if (len > 0) {
|
||||
text = std::string(buf, len < sizeof(buf) ? len : std::strlen(buf));
|
||||
}
|
||||
ov->HideKeyboard();
|
||||
currentText_.clear();
|
||||
existingText_.clear();
|
||||
userTyped_ = false;
|
||||
PIXELVR_LOG_INFO("VrKeyboard: User submitted text via KeyboardDone: '%s'", text.c_str());
|
||||
if (onDone) {
|
||||
onDone(text);
|
||||
@@ -382,27 +400,16 @@ void VrKeyboard::poll(std::function<void(const std::string&)> onDone,
|
||||
event.eventType == vr::VREvent_KeyboardClosed_Global) {
|
||||
isOpen_ = false;
|
||||
kbConfigured_ = false;
|
||||
ov->HideKeyboard();
|
||||
PIXELVR_LOG_INFO("VrKeyboard: Keyboard closed (doneSubmitted=%d)", doneSubmitted_ ? 1 : 0);
|
||||
if (!doneSubmitted_) {
|
||||
// On Steam Frame / SteamOS, pressing the checkmark or closing the keyboard issues KeyboardClosed
|
||||
// without KeyboardDone. Read the buffer and treat non-empty / modified input as submit!
|
||||
char buf[1024] = {0};
|
||||
uint32_t len = ov->GetKeyboardText(buf, sizeof(buf));
|
||||
std::string text = currentText_;
|
||||
if (len > 0) {
|
||||
text = std::string(buf, len < sizeof(buf) ? len : std::strlen(buf));
|
||||
}
|
||||
if (!text.empty() || text != existingText_) {
|
||||
PIXELVR_LOG_INFO("VrKeyboard: Treating KeyboardClosed as submit with text: '%s'", text.c_str());
|
||||
doneSubmitted_ = true;
|
||||
if (onDone) {
|
||||
onDone(text);
|
||||
}
|
||||
} else {
|
||||
PIXELVR_LOG_INFO("VrKeyboard: Keyboard cancelled without text change");
|
||||
if (onCancel) {
|
||||
onCancel();
|
||||
}
|
||||
// Closed without KeyboardDone: treat as cancel and forget any input
|
||||
currentText_.clear();
|
||||
existingText_.clear();
|
||||
userTyped_ = false;
|
||||
PIXELVR_LOG_INFO("VrKeyboard: Discarded input on KeyboardClosed");
|
||||
if (onCancel) {
|
||||
onCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,15 +419,19 @@ void VrKeyboard::poll(std::function<void(const std::string&)> onDone,
|
||||
if (isOpen_) {
|
||||
pollFramesSinceOpen_++;
|
||||
// Maintain and reinforce keyboard overlay configuration across the first 90 frames (~1 sec)
|
||||
// to catch asynchronous creation by SteamVR's GamepadUI
|
||||
if (!kbConfigured_ || pollFramesSinceOpen_ < 90) {
|
||||
// until successfully configured
|
||||
if (!kbConfigured_ && pollFramesSinceOpen_ < 90) {
|
||||
configureKeyboardOverlay();
|
||||
}
|
||||
|
||||
char liveBuf[1024] = {0};
|
||||
uint32_t liveLen = ov->GetKeyboardText(liveBuf, sizeof(liveBuf));
|
||||
if (liveLen > 0) {
|
||||
currentText_ = std::string(liveBuf, liveLen < sizeof(liveBuf) ? liveLen : std::strlen(liveBuf));
|
||||
// Only synchronize with SteamVR live buffer if the user has actively typed in this session.
|
||||
// This avoids contaminating a fresh/empty input field with stale residual buffer from a previous field.
|
||||
if (userTyped_) {
|
||||
char liveBuf[1024] = {0};
|
||||
uint32_t liveLen = ov->GetKeyboardText(liveBuf, sizeof(liveBuf));
|
||||
if (liveLen > 0) {
|
||||
currentText_ = std::string(liveBuf, liveLen < sizeof(liveBuf) ? liveLen : std::strlen(liveBuf));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ private:
|
||||
bool available_ = false;
|
||||
bool isOpen_ = false;
|
||||
bool doneSubmitted_ = false;
|
||||
bool userTyped_ = false;
|
||||
bool kbConfigured_ = false;
|
||||
int pollFramesSinceOpen_ = 0;
|
||||
float kbMat_[3][4]{};
|
||||
|
||||
Reference in new issue
Block a user