mouse grab improvements
This commit is contained in:
parent
48a4ada4f9
commit
74f50e8837
|
@ -25,6 +25,7 @@ fragment_shader [[
|
|||
|
||||
// https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8
|
||||
// https://gist.github.com/bgolus/d49651f52b1dcf82f70421ba922ed064
|
||||
// grid by Ben Golus
|
||||
float PristineGrid(vec2 uv, vec2 lineWidth) {
|
||||
vec4 uvDDXY = vec4(dFdx(uv), dFdy(uv));
|
||||
vec2 uvDeriv = vec2(length(uvDDXY.xz), length(uvDDXY.yw));
|
||||
|
|
|
@ -685,7 +685,7 @@ struct StudioAppImpl final : StudioApp
|
|||
const Array<Action*>& getActions() override { return m_actions; }
|
||||
|
||||
|
||||
void guiBeginFrame() const
|
||||
void guiBeginFrame()
|
||||
{
|
||||
PROFILE_FUNCTION();
|
||||
|
||||
|
@ -702,7 +702,7 @@ struct StudioAppImpl final : StudioApp
|
|||
}
|
||||
io.DeltaTime = m_engine->getLastTimeDelta();
|
||||
|
||||
if (!m_cursor_captured) {
|
||||
if (!m_cursor_clipped) {
|
||||
const os::Point cp = os::getMouseScreenPos();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||
io.AddMousePosEvent((float)cp.x, (float)cp.y);
|
||||
|
@ -715,7 +715,7 @@ struct StudioAppImpl final : StudioApp
|
|||
|
||||
const ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
ImGui::NewFrame();
|
||||
if (!m_cursor_captured) {
|
||||
if (!m_cursor_clipped) {
|
||||
static ImGuiMouseCursor last_cursor = ImGuiMouseCursor_COUNT;
|
||||
if (imgui_cursor != last_cursor) {
|
||||
switch (imgui_cursor) {
|
||||
|
@ -730,6 +730,8 @@ struct StudioAppImpl final : StudioApp
|
|||
}
|
||||
}
|
||||
ImGui::PushFont(m_font);
|
||||
|
||||
if (os::getFocused() != m_main_window && m_cursor_clipped) unclipMouseCursor();
|
||||
}
|
||||
|
||||
u32 getDockspaceID() const override {
|
||||
|
@ -1111,7 +1113,17 @@ struct StudioAppImpl final : StudioApp
|
|||
|
||||
Gizmo::Config& getGizmoConfig() override { return m_gizmo_config; }
|
||||
|
||||
void setCursorCaptured(bool captured) override { m_cursor_captured = captured; }
|
||||
void clipMouseCursor() override { m_cursor_clipped = true; }
|
||||
void unclipMouseCursor() override {
|
||||
os::clipCursor(os::INVALID_WINDOW, {});
|
||||
m_cursor_clipped = false;
|
||||
}
|
||||
bool isMouseCursorClipped() const override {return m_cursor_clipped; }
|
||||
|
||||
void setMouseClipRect(os::WindowHandle win, const os::Rect &screen_rect) override {
|
||||
if (!m_cursor_clipped) return;
|
||||
os::clipCursor(win, screen_rect);
|
||||
}
|
||||
|
||||
void addEntity() {
|
||||
const EntityRef e = m_editor->addEntity();
|
||||
|
@ -3459,7 +3471,7 @@ struct StudioAppImpl final : StudioApp
|
|||
Gizmo::Config m_gizmo_config;
|
||||
|
||||
bool m_show_save_world_ui = false;
|
||||
bool m_cursor_captured = false;
|
||||
bool m_cursor_clipped = false;
|
||||
bool m_confirm_exit = false;
|
||||
bool m_confirm_load = false;
|
||||
bool m_confirm_new = false;
|
||||
|
|
|
@ -25,8 +25,10 @@ struct Action;
|
|||
struct ComponentUID;
|
||||
namespace Gizmo { struct Config; }
|
||||
namespace os {
|
||||
using WindowHandle = void*;
|
||||
enum class MouseButton;
|
||||
struct Event;
|
||||
struct Rect;
|
||||
}
|
||||
|
||||
struct LUMIX_EDITOR_API StudioApp {
|
||||
|
@ -126,11 +128,18 @@ struct LUMIX_EDITOR_API StudioApp {
|
|||
virtual float getFOV() const = 0;
|
||||
virtual void setFOV(float fov_radians) = 0;
|
||||
virtual Gizmo::Config& getGizmoConfig() = 0;
|
||||
virtual void setCursorCaptured(bool captured) = 0;
|
||||
virtual void saveSettings() = 0;
|
||||
virtual int getImGuiKey(int keycode) const = 0;
|
||||
virtual u32 getDockspaceID() const = 0;
|
||||
|
||||
// clip mouse cursor = keep it in specified rectangle
|
||||
// cursor is automatically unclipped when app is inactive
|
||||
virtual void clipMouseCursor() = 0;
|
||||
// some platforms can't clip to `screen_rect` so they ignore it and use just `win`'s client rectangle
|
||||
virtual void setMouseClipRect(os::WindowHandle win, const os::Rect &screen_rect) = 0;
|
||||
virtual void unclipMouseCursor() = 0;
|
||||
virtual bool isMouseCursorClipped() const = 0;
|
||||
|
||||
virtual Span<const os::Event> getEvents() const = 0;
|
||||
virtual ImFont* getDefaultFont() = 0;
|
||||
virtual ImFont* getBoldFont() = 0;
|
||||
|
|
|
@ -1209,7 +1209,7 @@ bool makePath(const char* path) {
|
|||
}
|
||||
|
||||
|
||||
void grabMouse(WindowHandle window) {
|
||||
void clipCursor(WindowHandle win, const Rect& rect) {
|
||||
if (window == INVALID_WINDOW) {
|
||||
XUngrabPointer(G.display, CurrentTime);
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ enum class MouseButton : i32 {
|
|||
MAX = 16
|
||||
};
|
||||
|
||||
struct Point { int x, y; };
|
||||
struct Rect { int left, top, width, height; };
|
||||
struct Point { i32 x, y; };
|
||||
struct Rect { i32 left, top, width, height; };
|
||||
|
||||
using WindowHandle = void*;
|
||||
constexpr WindowHandle INVALID_WINDOW = nullptr;
|
||||
|
@ -203,7 +203,9 @@ LUMIX_ENGINE_API u64 getLastModified(StringView file);
|
|||
LUMIX_ENGINE_API [[nodiscard]] bool makePath(const char* path);
|
||||
|
||||
LUMIX_ENGINE_API void setCursor(CursorType type);
|
||||
LUMIX_ENGINE_API void grabMouse(WindowHandle win);
|
||||
// clip mouse cursor to `rect`, on platforms, where this is not possible, clip to `win`
|
||||
// pass INVALID_WINDOW to disable clipping
|
||||
LUMIX_ENGINE_API void clipCursor(WindowHandle win, const Rect& rect);
|
||||
|
||||
LUMIX_ENGINE_API [[nodiscard]] bool getDropFile(const Event& event, int idx, Span<char> out);
|
||||
LUMIX_ENGINE_API int getDropFileCount(const Event& event);
|
||||
|
|
|
@ -71,7 +71,6 @@ struct EventQueue {
|
|||
};
|
||||
|
||||
static struct {
|
||||
WindowHandle grabbed_window = INVALID_WINDOW;
|
||||
EventQueue event_queue;
|
||||
Point relative_mode_pos = {};
|
||||
bool relative_mouse = false;
|
||||
|
@ -549,17 +548,6 @@ Point toScreen(WindowHandle win, int x, int y)
|
|||
return res;
|
||||
}
|
||||
|
||||
void updateGrabbedMouse() {
|
||||
if (G.grabbed_window == INVALID_WINDOW) {
|
||||
DEBUG_CHECK(ClipCursor(NULL));
|
||||
return;
|
||||
}
|
||||
|
||||
RECT rect;
|
||||
DEBUG_CHECK(GetWindowRect((HWND)G.grabbed_window, &rect));
|
||||
DEBUG_CHECK(ClipCursor(&rect));
|
||||
}
|
||||
|
||||
WindowHandle createWindow(const InitWindowArgs& args) {
|
||||
PROFILE_FUNCTION();
|
||||
WCharStr<MAX_PATH> cls_name("lunex_window");
|
||||
|
@ -592,25 +580,20 @@ WindowHandle createWindow(const InitWindowArgs& args) {
|
|||
e.win_move.x = (i16)LOWORD(lParam);
|
||||
e.win_move.y = (i16)HIWORD(lParam);
|
||||
G.event_queue.pushBack(e);
|
||||
updateGrabbedMouse();
|
||||
return 0;
|
||||
case WM_SIZE:
|
||||
e.type = Event::Type::WINDOW_SIZE;
|
||||
e.win_size.w = LOWORD(lParam);
|
||||
e.win_size.h = HIWORD(lParam);
|
||||
G.event_queue.pushBack(e);
|
||||
updateGrabbedMouse();
|
||||
return 0;
|
||||
case WM_CLOSE:
|
||||
e.type = Event::Type::WINDOW_CLOSE;
|
||||
G.event_queue.pushBack(e);
|
||||
if (hWnd == G.grabbed_window) G.grabbed_window = INVALID_WINDOW;
|
||||
updateGrabbedMouse();
|
||||
return 0;
|
||||
case WM_ACTIVATE:
|
||||
if (wParam == WA_INACTIVE) {
|
||||
showCursor(true);
|
||||
grabMouse(INVALID_WINDOW);
|
||||
G.key_states[(u32)os::Keycode::SHIFT] = false;
|
||||
G.key_states[(u32)os::Keycode::CTRL] = false;
|
||||
G.key_states[(u32)os::Keycode::ALT] = false;
|
||||
|
@ -622,7 +605,6 @@ WindowHandle createWindow(const InitWindowArgs& args) {
|
|||
e.type = Event::Type::FOCUS;
|
||||
e.focus.gained = wParam != WA_INACTIVE;
|
||||
G.event_queue.pushBack(e);
|
||||
updateGrabbedMouse();
|
||||
break;
|
||||
case WM_NCPAINT:
|
||||
case WM_NCACTIVATE:
|
||||
|
@ -1361,9 +1343,16 @@ bool makePath(const char* path)
|
|||
return error_code == ERROR_SUCCESS || error_code == ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
void grabMouse(WindowHandle win) {
|
||||
G.grabbed_window = win;
|
||||
updateGrabbedMouse();
|
||||
void clipCursor(WindowHandle win, const Rect& rect) {
|
||||
if (win == INVALID_WINDOW) ClipCursor(NULL);
|
||||
else {
|
||||
RECT wrect;
|
||||
wrect.left = rect.left;
|
||||
wrect.top = rect.top;
|
||||
wrect.bottom = rect.top + rect.height;
|
||||
wrect.right = rect.left + rect.width;
|
||||
ClipCursor(&wrect);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -112,14 +112,13 @@ void GameView::enableIngameCursor(bool enable)
|
|||
}
|
||||
|
||||
|
||||
void GameView::captureMouse(bool capture)
|
||||
{
|
||||
void GameView::captureMouse(bool capture) {
|
||||
if (m_is_mouse_captured == capture) return;
|
||||
|
||||
m_app.setCursorCaptured(capture);
|
||||
m_is_mouse_captured = capture;
|
||||
os::showCursor(!capture || m_is_ingame_cursor);
|
||||
if (!capture) os::grabMouse(os::INVALID_WINDOW);
|
||||
if (capture) m_app.clipMouseCursor();
|
||||
else m_app.unclipMouseCursor();
|
||||
}
|
||||
|
||||
void GameView::onSettingsLoaded() {
|
||||
|
@ -245,7 +244,7 @@ void GameView::onGUI()
|
|||
}
|
||||
m_was_game_mode = is_game_mode;
|
||||
|
||||
if (m_is_mouse_captured && (ImGui::IsKeyDown(ImGuiKey_Escape) || !editor.isGameMode())) {
|
||||
if (m_is_mouse_captured && (ImGui::IsKeyDown(ImGuiKey_Escape) || !editor.isGameMode() || !m_app.isMouseCursorClipped())) {
|
||||
captureMouse(false);
|
||||
}
|
||||
|
||||
|
@ -267,15 +266,13 @@ void GameView::onGUI()
|
|||
|
||||
if (!m_pipeline->isReady()) captureMouse(false);
|
||||
|
||||
ImVec2 view_pos;
|
||||
ImVec2 view_size;
|
||||
bool is_game_view_visible = false;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
if (ImGui::Begin(window_name, &m_is_open, ImGuiWindowFlags_NoNavInputs)) {
|
||||
is_game_view_visible = true;
|
||||
view_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
const ImVec2 content_min = view_pos;
|
||||
const ImVec2 content_min = ImGui::GetCursorScreenPos();
|
||||
view_size = ImGui::GetContentRegionAvail();
|
||||
view_size.y -= ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y * 3;
|
||||
ImVec2 content_max(content_min.x + view_size.x, content_min.y + view_size.y);
|
||||
|
@ -306,6 +303,7 @@ void GameView::onGUI()
|
|||
|
||||
controlsGUI(editor);
|
||||
|
||||
const ImVec2 view_pos = ImGui::GetCursorScreenPos();
|
||||
if (texture_handle) {
|
||||
if (gpu::isOriginBottomLeft()) {
|
||||
ImGui::Image(texture_handle, view_size, ImVec2(0, 1), ImVec2(1, 0));
|
||||
|
@ -317,6 +315,15 @@ void GameView::onGUI()
|
|||
else {
|
||||
ImGuiEx::Rect(view_size.x, view_size.y, 0xffFF00FF);
|
||||
}
|
||||
if (m_is_mouse_captured) {
|
||||
os::Rect rect;
|
||||
rect.left = (i32)view_pos.x;
|
||||
rect.top = (i32)view_pos.y;
|
||||
rect.width = (i32)view_size.x;
|
||||
rect.height = (i32)view_size.y;
|
||||
m_app.setMouseClipRect(ImGui::GetWindowViewport()->PlatformHandle, rect);
|
||||
}
|
||||
|
||||
const bool is_hovered = ImGui::IsItemHovered();
|
||||
if (is_hovered && ImGui::IsMouseReleased(0) && editor.isGameMode()) captureMouse(true);
|
||||
m_pos = ImGui::GetItemRectMin();
|
||||
|
@ -327,10 +334,8 @@ void GameView::onGUI()
|
|||
|
||||
}
|
||||
|
||||
if (m_is_mouse_captured && os::getFocused() != ImGui::GetWindowViewport()->PlatformHandle) captureMouse(false);
|
||||
if (m_is_mouse_captured && is_game_view_visible) {
|
||||
os::grabMouse(ImGui::GetWindowViewport()->PlatformHandle);
|
||||
}
|
||||
if (m_is_mouse_captured && !is_game_view_visible) captureMouse(false);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
|
|
@ -1060,20 +1060,18 @@ void SceneView::renderGizmos()
|
|||
}
|
||||
|
||||
|
||||
void SceneView::captureMouse(bool capture)
|
||||
{
|
||||
if(m_is_mouse_captured == capture) return;
|
||||
void SceneView::captureMouse(bool capture) {
|
||||
if (m_is_mouse_captured == capture) return;
|
||||
m_is_mouse_captured = capture;
|
||||
m_app.setCursorCaptured(capture);
|
||||
os::showCursor(!m_is_mouse_captured);
|
||||
os::showCursor(!capture);
|
||||
if (capture) {
|
||||
os::grabMouse(ImGui::GetWindowViewport()->PlatformHandle);
|
||||
m_app.clipMouseCursor();
|
||||
const os::Point p = os::getMouseScreenPos();
|
||||
m_captured_mouse_x = p.x;
|
||||
m_captured_mouse_y = p.y;
|
||||
}
|
||||
else {
|
||||
os::grabMouse(os::INVALID_WINDOW);
|
||||
m_app.unclipMouseCursor();
|
||||
os::setMouseScreenPos(m_captured_mouse_x, m_captured_mouse_y);
|
||||
}
|
||||
}
|
||||
|
@ -1260,7 +1258,7 @@ void SceneView::onToolbar()
|
|||
}
|
||||
|
||||
void SceneView::handleEvents() {
|
||||
const bool handle_input = m_is_mouse_captured || (ImGui::IsItemHovered() && os::getFocused() == ImGui::GetWindowViewport()->PlatformHandle);
|
||||
const bool handle_input = m_is_mouse_captured || ImGui::IsItemHovered();
|
||||
for (const os::Event event : m_app.getEvents()) {
|
||||
switch (event.type) {
|
||||
case os::Event::Type::KEY: {
|
||||
|
@ -1410,6 +1408,8 @@ void SceneView::insertModelUI() {
|
|||
|
||||
void SceneView::onGUI()
|
||||
{
|
||||
if (m_is_mouse_captured && !m_app.isMouseCursorClipped()) captureMouse(false);
|
||||
|
||||
m_has_focus = false;
|
||||
PROFILE_FUNCTION();
|
||||
m_pipeline->setWorld(m_editor.getWorld());
|
||||
|
@ -1420,6 +1420,7 @@ void SceneView::onGUI()
|
|||
if (m_log_ui.getUnreadErrorCount() > 0) title = ICON_FA_GLOBE "Scene View | " ICON_FA_EXCLAMATION_TRIANGLE " errors in log###Scene View";
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImVec2 view_size;
|
||||
if (ImGui::Begin(title, nullptr, ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
m_has_focus = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) || m_has_focus;
|
||||
|
||||
|
@ -1427,10 +1428,10 @@ void SceneView::onGUI()
|
|||
is_open = true;
|
||||
ImGui::Dummy(ImVec2(2, 2));
|
||||
onToolbar();
|
||||
const ImVec2 size = ImGui::GetContentRegionAvail();
|
||||
view_size = ImGui::GetContentRegionAvail();
|
||||
Viewport vp = m_view->getViewport();
|
||||
vp.w = (int)size.x;
|
||||
vp.h = (int)size.y;
|
||||
vp.w = (int)view_size.x;
|
||||
vp.h = (int)view_size.y;
|
||||
m_view->setViewport(vp);
|
||||
m_pipeline->setViewport(vp);
|
||||
m_pipeline->render(false);
|
||||
|
@ -1439,27 +1440,27 @@ void SceneView::onGUI()
|
|||
m_view->inputFrame();
|
||||
|
||||
const gpu::TextureHandle texture_handle = m_pipeline->getOutput();
|
||||
if (size.x > 0 && size.y > 0) {
|
||||
if (view_size.x > 0 && view_size.y > 0) {
|
||||
const ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
|
||||
m_screen_x = int(cursor_pos.x);
|
||||
m_screen_y = int(cursor_pos.y);
|
||||
m_width = int(size.x);
|
||||
m_height = int(size.y);
|
||||
m_width = int(view_size.x);
|
||||
m_height = int(view_size.y);
|
||||
view_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
if (texture_handle) {
|
||||
void* t = texture_handle;
|
||||
if (gpu::isOriginBottomLeft()) {
|
||||
ImGui::Image(t, size, ImVec2(0, 1), ImVec2(1, 0));
|
||||
ImGui::Image(t, view_size, ImVec2(0, 1), ImVec2(1, 0));
|
||||
}
|
||||
else {
|
||||
ImGui::Image(t, size);
|
||||
ImGui::Image(t, view_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (auto* payload = ImGui::AcceptDragDropPayload("path")) {
|
||||
const ImVec2 drop_pos = (ImGui::GetMousePos() - view_pos) / size;
|
||||
const ImVec2 drop_pos = (ImGui::GetMousePos() - view_pos) / view_size;
|
||||
handleDrop((const char*)payload->Data, drop_pos.x, drop_pos.y);
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
|
@ -1475,13 +1476,17 @@ void SceneView::onGUI()
|
|||
m_view->m_draw_cmds.clear();
|
||||
}
|
||||
|
||||
if (m_is_mouse_captured && os::getFocused() != ImGui::GetWindowViewport()->PlatformHandle) {
|
||||
captureMouse(false);
|
||||
}
|
||||
|
||||
|
||||
ImGui::End();
|
||||
|
||||
if (m_is_mouse_captured && is_open) {
|
||||
os::Rect rect;
|
||||
rect.left = (i32)view_pos.x;
|
||||
rect.top = (i32)view_pos.y;
|
||||
rect.width = (i32)view_size.x;
|
||||
rect.height = (i32)view_size.y;
|
||||
m_app.setMouseClipRect(ImGui::GetWindowViewport()->PlatformHandle, rect);
|
||||
}
|
||||
|
||||
insertModelUI();
|
||||
|
||||
if (is_open && m_is_measure_active) {
|
||||
|
|
|
@ -157,19 +157,28 @@ void WorldViewer::gui() {
|
|||
m_pipeline->setViewport(vp);
|
||||
m_pipeline->render(false);
|
||||
gpu::TextureHandle preview = m_pipeline->getOutput();
|
||||
const ImVec2 view_pos = ImGui::GetCursorScreenPos();
|
||||
if (gpu::isOriginBottomLeft()) {
|
||||
ImGui::Image(preview, image_size);
|
||||
}
|
||||
else {
|
||||
ImGui::Image(preview, image_size, ImVec2(0, 1), ImVec2(1, 0));
|
||||
}
|
||||
|
||||
|
||||
if (m_is_mouse_captured) {
|
||||
os::Rect rect;
|
||||
rect.left = (i32)view_pos.x;
|
||||
rect.top = (i32)view_pos.y;
|
||||
rect.width = (i32)image_size.x;
|
||||
rect.height = (i32)image_size.y;
|
||||
m_app.setMouseClipRect(ImGui::GetWindowViewport()->PlatformHandle, rect);
|
||||
}
|
||||
|
||||
const bool mouse_down = ImGui::IsMouseDown(ImGuiMouseButton_Right);
|
||||
if (m_is_mouse_captured && !mouse_down) {
|
||||
if (m_is_mouse_captured && (!mouse_down || !m_app.isMouseCursorClipped())) {
|
||||
m_is_mouse_captured = false;
|
||||
m_app.setCursorCaptured(false);
|
||||
m_app.unclipMouseCursor();
|
||||
os::showCursor(true);
|
||||
os::grabMouse(os::INVALID_WINDOW);
|
||||
os::setMouseScreenPos(m_captured_mouse_pos.x, m_captured_mouse_pos.y);
|
||||
}
|
||||
|
||||
|
@ -183,9 +192,8 @@ void WorldViewer::gui() {
|
|||
|
||||
if (!m_is_mouse_captured) {
|
||||
m_is_mouse_captured = true;
|
||||
m_app.setCursorCaptured(true);
|
||||
m_app.clipMouseCursor();
|
||||
os::showCursor(false);
|
||||
os::grabMouse(ImGui::GetWindowViewport()->PlatformHandle);
|
||||
m_captured_mouse_pos = os::getMouseScreenPos();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue