Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Engine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,6 @@ GitHub access to this repository and are retained for 30 days.

Explorer rename, clipboard, drag and script-scope controls are described in [`docs/EXPLORER.md`](docs/EXPLORER.md).

3D camera controls and keyboard-focus troubleshooting are described in [`docs/VIEWPORT_NAVIGATION.md`](docs/VIEWPORT_NAVIGATION.md).

Gameplay authors should start with [`docs/GAMEPLAY_PROGRAMMING.md`](docs/GAMEPLAY_PROGRAMMING.md).
10 changes: 3 additions & 7 deletions Engine/apps/editor/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod explorer_selection;
mod play_session;
mod ui_theme;
mod viewport_keyboard;

use eframe::egui;
use egui_tiles::{Behavior, TileId, Tree, UiResponse};
Expand Down Expand Up @@ -1646,8 +1647,7 @@ impl EditorViewer<'_> {
if response.hovered() && ui.input(|input| input.pointer.any_pressed()) {
response.request_focus();
}
let keyboard_active =
(response.has_focus() || response.hovered()) && !ui.ctx().egui_wants_keyboard_input();
let keyboard_active = viewport_keyboard::active(response);
let keys = if keyboard_active {
ui.input(|input| {
let mut keys = BTreeSet::new();
Expand Down Expand Up @@ -1712,11 +1712,7 @@ impl EditorViewer<'_> {
self.request_preview = true;
}
}
// Hovering is sufficient to steer the scene. Requiring an earlier LMB
// click made RMB-look and WASD appear broken because the viewport never
// acquired keyboard focus.
let keyboard_active =
(response.has_focus() || hovered) && !ui.ctx().egui_wants_keyboard_input();
let keyboard_active = viewport_keyboard::active(response);
if keyboard_active {
ui.input(|input| {
if input.key_pressed(egui::Key::F) {
Expand Down
79 changes: 79 additions & 0 deletions Engine/apps/editor/src/viewport_keyboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use eframe::egui;

pub fn active(response: &egui::Response) -> bool {
// egui wants keyboard input for any focused widget, including this viewport.
// Give the viewport its own keys while respecting focus in other widgets.
response.ctx.input(|input| input.focused)
&& (response.has_focus()
|| (response.hovered() && !response.ctx.egui_wants_keyboard_input()))
}

#[cfg(test)]
mod tests {
use super::*;

#[derive(Clone, Copy)]
enum Focus {
None,
Viewport,
Text,
}

fn keyboard_active(focus: Focus, hovered: bool, window_focused: bool) -> bool {
let ctx = egui::Context::default();
let mut active = false;
let mut text = String::new();
// Warm up hit testing before checking the hovered response.
for _ in 0..2 {
let mut output = ctx.run_ui(
egui::RawInput {
screen_rect: Some(egui::Rect::from_min_size(
egui::Pos2::ZERO,
egui::vec2(800.0, 600.0),
)),
focused: window_focused,
events: vec![egui::Event::PointerMoved(if hovered {
egui::pos2(50.0, 50.0)
} else {
egui::pos2(700.0, 500.0)
})],
..Default::default()
},
|ui| {
let (_, viewport) = ui.allocate_exact_size(
egui::vec2(200.0, 200.0),
egui::Sense::click_and_drag(),
);
let field = ui.text_edit_singleline(&mut text);
match focus {
Focus::None => {}
Focus::Viewport => viewport.request_focus(),
Focus::Text => field.request_focus(),
}
active = super::active(&viewport);
},
);
output.textures_delta.clear();
}
active
}

#[test]
fn clicking_viewport_does_not_block_its_keyboard_controls() {
assert!(keyboard_active(Focus::Viewport, true, true));
assert!(keyboard_active(Focus::Viewport, false, true));
}

#[test]
fn hovering_allows_navigation_without_a_prior_click() {
assert!(keyboard_active(Focus::None, true, true));
assert!(!keyboard_active(Focus::None, false, true));
}

#[test]
fn typing_and_window_blur_do_not_drive_the_viewport() {
assert!(!keyboard_active(Focus::Text, true, true));
assert!(!keyboard_active(Focus::Viewport, true, false));
assert!(!keyboard_active(Focus::None, true, false));
}
}
30 changes: 30 additions & 0 deletions Engine/docs/VIEWPORT_NAVIGATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 3D editor navigation

Open a project and stop Play before navigating the authoring camera. Open a missing
3D viewport through **View**. Hover the viewport to navigate without a prior click,
or click it to give it keyboard focus. A focused viewport keeps receiving navigation
keys when the pointer moves outside it. Text fields in other panels keep their own
keyboard input until you click the viewport. Navigation pauses when the editor
window loses focus.

| Control | Action |
| --- | --- |
| W / S | Move forward / backward. |
| A / D | Move left / right. |
| Q / E | Pan left / right horizontally. |
| Middle, right or left drag away from a gizmo | Orbit. |
| Shift + middle drag | Pan. |
| Mouse wheel | Dolly. |
| F | Frame the selected object. |
| Home | Reset the editor camera. |

Hold movement keys for continuous navigation; release them to stop. Only the
authoring camera moves. These controls do not move objects or the scene camera,
and keyboard navigation is unavailable in the 2D authoring viewport. During Play,
WASD is forwarded to gameplay scripts instead of navigating the authoring camera.

If WASD or Q/E does not respond, stop Play, activate the editor window and click
inside the 3D viewport to leave any Inspector or Explorer rename field. Use
**Home** to reset the view or select an object in the hierarchy and press **F**.

The corresponding website guide is **Editor overview > Viewport tools**.
Loading