From cabccae4d7a57dde4f4fbb491d57b58541661e93 Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sat, 30 May 2020 18:34:35 +0200 Subject: [PATCH 01/11] Reduce warnings (unused-parameter, reorder) --- ImgWindow.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ImgWindow.cpp b/ImgWindow.cpp index e9e3817..51bcd5c 100644 --- a/ImgWindow.cpp +++ b/ImgWindow.cpp @@ -52,10 +52,10 @@ ImgWindow::ImgWindow( int bottom, XPLMWindowDecoration decoration, XPLMWindowLayer layer) : + mFirstRender(true), + mFontAtlas(sFontAtlas), mIsInVR(false), - mPreferredLayer(layer), - mFirstRender(true), - mFontAtlas(sFontAtlas) + mPreferredLayer(layer) { ImFontAtlas *iFontAtlas = nullptr; if (mFontAtlas) { @@ -402,12 +402,12 @@ ImgWindow::HandleMouseClickGeneric(int x, int y, XPLMMouseStatus inMouse, int bu void ImgWindow::HandleKeyFuncCB( - XPLMWindowID inWindowID, + XPLMWindowID /*inWindowID*/, char inKey, XPLMKeyFlags inFlags, char inVirtualKey, void * inRefcon, - int losingFocus) + int /*losingFocus*/) { auto *thisWindow = reinterpret_cast(inRefcon); ImGui::SetCurrentContext(thisWindow->mImGuiContext); @@ -431,7 +431,7 @@ ImgWindow::HandleKeyFuncCB( XPLMCursorStatus ImgWindow::HandleCursorFuncCB( - XPLMWindowID inWindowID, + XPLMWindowID /*inWindowID*/, int x, int y, void * inRefcon) @@ -448,7 +448,7 @@ ImgWindow::HandleCursorFuncCB( int ImgWindow::HandleMouseWheelFuncCB( - XPLMWindowID inWindowID, + XPLMWindowID /*inWindowID*/, int x, int y, int wheel, @@ -558,10 +558,10 @@ std::queue ImgWindow::sPendingDestruction; XPLMFlightLoopID ImgWindow::sSelfDestructHandler = nullptr; float -ImgWindow::SelfDestructCallback(float inElapsedSinceLastCall, - float inElapsedTimeSinceLastFlightLoop, - int inCounter, - void *inRefcon) +ImgWindow::SelfDestructCallback(float /*inElapsedSinceLastCall*/, + float /*inElapsedTimeSinceLastFlightLoop*/, + int /*inCounter*/, + void* /*inRefcon*/) { while (!sPendingDestruction.empty()) { auto *thisObj = sPendingDestruction.front(); From d3ce83ec29a81e66d60c57f966bd535a9d3d0eac Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sat, 13 Jun 2020 21:08:15 +0200 Subject: [PATCH 02/11] Public functions to work with the XP window; new hook `beforeBegin` --- ImgWindow.cpp | 11 ++++------- ImgWindow.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/ImgWindow.cpp b/ImgWindow.cpp index 51bcd5c..a0eeed8 100644 --- a/ImgWindow.cpp +++ b/ImgWindow.cpp @@ -54,7 +54,6 @@ ImgWindow::ImgWindow( XPLMWindowLayer layer) : mFirstRender(true), mFontAtlas(sFontAtlas), - mIsInVR(false), mPreferredLayer(layer) { ImFontAtlas *iFontAtlas = nullptr; @@ -334,7 +333,7 @@ ImgWindow::updateImgui() ImGui::SetNextWindowSize(ImVec2(win_width, win_height), ImGuiCond_Always); // and construct the window - ImGui::Begin(mWindowTitle.c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + ImGui::Begin(mWindowTitle.c_str(), nullptr, beforeBegin() | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); buildInterface(); ImGui::End(); @@ -464,10 +463,10 @@ ImgWindow::HandleMouseWheelFuncCB( io.MousePos = ImVec2(outX, outY); switch (wheel) { case 0: - io.MouseWheel = static_cast(clicks); + io.MouseWheel += static_cast(clicks); break; case 1: - io.MouseWheelH = static_cast(clicks); + io.MouseWheelH += static_cast(clicks); break; default: // unknown wheel @@ -516,11 +515,9 @@ ImgWindow::moveForVR() // - if we're VR enabled, explicitly move the window to the VR world. if (XPLMGetDatai(gVrEnabledRef)) { XPLMSetWindowPositioningMode(mWindowID, xplm_WindowVR, 0); - mIsInVR = true; } else { - if (mIsInVR) { + if (IsInVR()) { XPLMSetWindowPositioningMode(mWindowID, mPreferredLayer, -1); - mIsInVR = false; } } } diff --git a/ImgWindow.h b/ImgWindow.h index b7c13d1..9ae7b4a 100644 --- a/ImgWindow.h +++ b/ImgWindow.h @@ -84,6 +84,30 @@ ImgWindow { static std::shared_ptr sFontAtlas; virtual ~ImgWindow(); + + /** Gets the current window geometry */ + void GetWindowGeometry (int& left, int& top, int& right, int& bottom) const + { XPLMGetWindowGeometry(mWindowID, &left, &top, &right, &bottom); } + + /** Sets the current window geometry */ + void SetWindowGeometry (int left, int top, int right, int bottom) + { XPLMSetWindowGeometry(mWindowID, left, top, right, bottom); } + + /** Gets the current window geometry of a popped out window */ + void GetWindowGeometryOS (int& left, int& top, int& right, int& bottom) const + { XPLMGetWindowGeometryOS(mWindowID, &left, &top, &right, &bottom); } + + /** Sets the current window geometry of a popped out window */ + void SetWindowGeometryOS (int left, int top, int right, int bottom) + { XPLMSetWindowGeometryOS(mWindowID, left, top, right, bottom); } + + /** Gets the current window size of window in VR */ + void GetWindowGeometryVR (int& width, int& height) const + { XPLMGetWindowGeometryVR(mWindowID, &width, &height); } + + /** Sets the current window size of window in VR */ + void SetWindowGeometryVR (int width, int height) + { XPLMSetWindowGeometryVR(mWindowID, width, height); } /** SetVisible() makes the window visible after making the onShow() call. * It is also at this time that the window will be relocated onto the VR @@ -98,7 +122,25 @@ ImgWindow { * @return true if the window is visible, false otherwise. */ bool GetVisible() const; - + + /** Is Window popped out */ + bool IsPoppedOut () const { return XPLMWindowIsPoppedOut(mWindowID) != 0; } + + /** Is Window in VR? */ + bool IsInVR () const { return XPLMWindowIsInVR(mWindowID) != 0; } + + /** Set the positioning mode + * @see https://developer.x-plane.com/sdk/XPLMDisplay/#XPLMWindowPositioningMode */ + void SetWindowPositioningMode (XPLMWindowPositioningMode inPosMode, + int inMonitorIdx = -1) + { XPLMSetWindowPositioningMode (mWindowID, inPosMode, inMonitorIdx); } + + /** Bring window to front of Z-order */ + void BringWindowToFront () { XPLMBringWindowToFront(mWindowID); } + + /** Is Window in front of Z-order? */ + bool IsWindowInFront () const { return XPLMIsWindowInFront(mWindowID) != 0; } + protected: /** mFirstRender can be checked during buildInterface() to see if we're * being rendered for the first time or not. This is particularly @@ -146,6 +188,12 @@ ImgWindow { * preferred layer or the VR layer depending on if the headset is in use. */ void moveForVR(); + + /** A hook called before imgui::begin in case you want to set up something + * before interface building begins + * @return addition flags to be passed to the imgui::begin() call, + * like for example ImGuiWindowFlags_MenuBar */ + virtual ImGuiWindowFlags_ beforeBegin() { return ImGuiWindowFlags_None; } /** buildInterface() is the method where you can define your ImGui interface * and handle events. It is called every frame the window is drawn. @@ -170,6 +218,9 @@ ImgWindow { * self-delete once it's finished rendering this frame. */ void SafeDelete(); + + /** Returns X-Plane's internal Window id */ + XPLMWindowID GetWindowId () const { return mWindowID; } private: std::shared_ptr mFontAtlas; @@ -240,7 +291,6 @@ ImgWindow { XPLMWindowID mWindowID; ImGuiContext *mImGuiContext; GLuint mFontTexture; - bool mIsInVR; int mTop; int mBottom; From 96e3a684f47dab3bfdfac1ecab570b2dcc1d2abd Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sat, 13 Jun 2020 21:11:07 +0200 Subject: [PATCH 03/11] Added `afterRendering` hook --- ImgWindow.cpp | 3 +++ ImgWindow.h | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ImgWindow.cpp b/ImgWindow.cpp index a0eeed8..a0e328c 100644 --- a/ImgWindow.cpp +++ b/ImgWindow.cpp @@ -363,6 +363,9 @@ ImgWindow::DrawWindowCB(XPLMWindowID /* inWindowID */, void *inRefcon) ImGui::Render(); thisWindow->RenderImGui(ImGui::GetDrawData()); + + // Give subclasses a chance to do something after all rendering + thisWindow->afterRendering(); } int diff --git a/ImgWindow.h b/ImgWindow.h index 9ae7b4a..d0c25ae 100644 --- a/ImgWindow.h +++ b/ImgWindow.h @@ -189,7 +189,7 @@ ImgWindow { */ void moveForVR(); - /** A hook called before imgui::begin in case you want to set up something + /** A hook called right before ImGui::Begin in case you want to set up something * before interface building begins * @return addition flags to be passed to the imgui::begin() call, * like for example ImGuiWindowFlags_MenuBar */ @@ -203,6 +203,11 @@ ImgWindow { */ virtual void buildInterface() = 0; + /** A hook called after all rendering is done, right before the + * X-Plane window draw call back returns + * in case you want to do something that otherwise would conflict with rendering. */ + virtual void afterRendering() {} + /** onShow() is called before making the Window visible. It provides an * opportunity to prevent the window being shown. * From ed2635c4cf59eec61d9fc538eda9129f21012e12 Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sat, 13 Jun 2020 21:12:32 +0200 Subject: [PATCH 04/11] Removed dependency on XPCProcessing.h --- ImgWindow.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ImgWindow.h b/ImgWindow.h index d0c25ae..893808e 100644 --- a/ImgWindow.h +++ b/ImgWindow.h @@ -36,13 +36,12 @@ #define IMGWINDOW_H #include "SystemGL.h" -#include "XOGLUtils.h" #include #include #include -#include +#include #include #include From 380ff5f6ac15489007250ae8dda072edb57b1421 Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sat, 13 Jun 2020 21:14:59 +0200 Subject: [PATCH 05/11] Prevent copying of an ImgWindow object as underlying ImGui resources don't copy well. Done by deleting the implicitly declared copy constructor/assignment. --- ImgWindow.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ImgWindow.h b/ImgWindow.h index 893808e..4811b89 100644 --- a/ImgWindow.h +++ b/ImgWindow.h @@ -175,6 +175,11 @@ ImgWindow { int bottom, XPLMWindowDecoration decoration = xplm_WindowDecorationRoundRectangle, XPLMWindowLayer layer = xplm_WindowLayerFloatingWindows); + + /** An ImgWindow object must not be copied! + */ + ImgWindow (const ImgWindow&) = delete; + ImgWindow& operator = (const ImgWindow&) = delete; /** SetWindowTitle sets the title of the window both in the ImGui layer and * at the XPLM layer. From 95603ed97b46dac5f8a2c6e63b3914cb39ccc355 Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sat, 13 Jun 2020 21:16:40 +0200 Subject: [PATCH 06/11] Added GetCurrentWindowGeometry() returning the currently valid geometry (OS, VR, or just standard) --- ImgWindow.cpp | 13 +++++++++++++ ImgWindow.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/ImgWindow.cpp b/ImgWindow.cpp index a0e328c..4a40df5 100644 --- a/ImgWindow.cpp +++ b/ImgWindow.cpp @@ -176,6 +176,19 @@ ImgWindow::~ImgWindow() XPLMDestroyWindow(mWindowID); } +void +ImgWindow::GetCurrentWindowGeometry (int& left, int& top, int& right, int& bottom) const +{ + if (IsPoppedOut()) + GetWindowGeometryOS(left, top, right, bottom); + else if (IsInVR()) { + left = bottom = 0; + GetWindowGeometryVR(right, top); + } else { + GetWindowGeometry(left, top, right, bottom); + } +} + void ImgWindow::updateMatrices() { diff --git a/ImgWindow.h b/ImgWindow.h index 4811b89..ce40777 100644 --- a/ImgWindow.h +++ b/ImgWindow.h @@ -107,6 +107,10 @@ ImgWindow { /** Sets the current window size of window in VR */ void SetWindowGeometryVR (int width, int height) { XPLMSetWindowGeometryVR(mWindowID, width, height); } + + /** Gets the current valid geometry (free, OS, or VR + If VR, then left=bottom=0 and right=width and top=height*/ + void GetCurrentWindowGeometry (int& left, int& top, int& right, int& bottom) const; /** SetVisible() makes the window visible after making the onShow() call. * It is also at this time that the window will be relocated onto the VR From 50ade83ba9efeafe3a80f85de6201bb81495b8bc Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sat, 13 Jun 2020 21:19:03 +0200 Subject: [PATCH 07/11] Move/Resize support for SelfDecoratedResizable windows Can now define a "drag area": Clicking into that area of a window and dragging moves the entire window. Useful especially for self-decorated windows. --- ImgWindow.cpp | 189 +++++++++++++++++++++++++++++++++++++++++++++----- ImgWindow.h | 73 +++++++++++++++++++ 2 files changed, 244 insertions(+), 18 deletions(-) diff --git a/ImgWindow.cpp b/ImgWindow.cpp index 4a40df5..d6a90b9 100644 --- a/ImgWindow.cpp +++ b/ImgWindow.cpp @@ -38,6 +38,12 @@ #include #include +// size of "frame" around a resizable window, by which its size can be changed +constexpr int WND_RESIZE_LEFT_WIDTH = 15; +constexpr int WND_RESIZE_TOP_WIDTH = 5; +constexpr int WND_RESIZE_RIGHT_WIDTH = 15; +constexpr int WND_RESIZE_BOTTOM_WIDTH = 15; + static XPLMDataRef gVrEnabledRef = nullptr; static XPLMDataRef gModelviewMatrixRef = nullptr; static XPLMDataRef gViewportRef = nullptr; @@ -54,7 +60,8 @@ ImgWindow::ImgWindow( XPLMWindowLayer layer) : mFirstRender(true), mFontAtlas(sFontAtlas), - mPreferredLayer(layer) + mPreferredLayer(layer), + bHandleWndResize(xplm_WindowDecorationSelfDecoratedResizable == decoration) { ImFontAtlas *iFontAtlas = nullptr; if (mFontAtlas) { @@ -189,6 +196,16 @@ ImgWindow::GetCurrentWindowGeometry (int& left, int& top, int& right, int& botto } } +void +ImgWindow::SetWindowResizingLimits (int minW, int minH, int maxW, int maxH) +{ + minWidth = minW; + minHeight = minH; + maxWidth = maxW; + maxHeight = maxH; + XPLMSetWindowResizingLimits(mWindowID, minW, minH, maxW, maxH); +} + void ImgWindow::updateMatrices() { @@ -394,24 +411,112 @@ ImgWindow::HandleMouseClickGeneric(int x, int y, XPLMMouseStatus inMouse, int bu ImGui::SetCurrentContext(mImGuiContext); ImGuiIO& io = ImGui::GetIO(); - float outX, outY; - translateToImguiSpace(x, y, outX, outY); - - io.MousePos = ImVec2(outX, outY); + // Tell ImGui the mous position relative to the window + translateToImguiSpace(x, y, io.MousePos.x, io.MousePos.y); + const int loc_x = int(io.MousePos.x); // local x, relative to top/left corner + const int loc_y = int(io.MousePos.y); + const int dx = x - lastMouseDragX; // dragged how far since last down/drag event? + const int dy = y - lastMouseDragY; + + switch (inMouse) { + + case xplm_MouseDrag: + io.MouseDown[button] = true; + + // Any kind of self-dragging/resizing only happens with a floating window in the sim + if (button == 0 && // left button + IsInsideSim() && // floating window in sim + dragWhat && // and if there actually _is_ dragging + (dx != 0 || dy != 0)) + { + // shall we drag the entire window? + if (dragWhat.wnd) + { + mLeft += dx; // move the wdinow + mRight += dx; + mTop += dy; + mBottom += dy; + } else { + // do we need to handle window resize? + if (dragWhat.left) mLeft += dx; + if (dragWhat.top) mTop += dy; + if (dragWhat.right) mRight += dx; + if (dragWhat.bottom) mBottom += dy; + + // Make sure resizing limits are honored + if (mRight-mLeft < minWidth) + { + if (dragWhat.left) mLeft = mRight - minWidth; + else mRight = mLeft + minWidth; + } + if (mRight-mLeft > maxWidth) + { + if (dragWhat.left) mLeft = mRight - maxWidth; + else mRight = mLeft + maxWidth; + } + if (mTop-mBottom < minHeight) { + if (dragWhat.top) mTop = mBottom + minHeight; + else mBottom = mTop - minHeight; + } + if (mTop-mBottom > maxHeight) { + if (dragWhat.top) mTop = mBottom + maxHeight; + else mBottom = mTop - maxHeight; + } + // FIXME: If we had to apply resizing restricitons, then mouse and window frame will now be out of synch + } + + // Change window geometry + SetWindowGeometry(mLeft, mTop, mRight, mBottom); + // now that the window has moved under the mouse we need to update relative mouse pos + translateToImguiSpace(x, y, io.MousePos.x, io.MousePos.y); + // Update the last handled position + lastMouseDragX = x; + lastMouseDragY = y; + } + break; + + case xplm_MouseDown: + io.MouseDown[button] = true; + + // Which part of the window would we drag, if any? + dragWhat.clear(); + if (button == 0 && // left button + IsInsideSim() && // floating window in simulator + loc_x >= 0 && loc_y >= 0) // valid local position + { + // shall we drag the entire window? + if (IsInsideWindowDragArea(loc_x, loc_y)) + { + dragWhat.wnd = true; + } + // do we need to handle window resize? + else if (bHandleWndResize) + { + dragWhat.left = loc_x <= WND_RESIZE_LEFT_WIDTH; + dragWhat.top = loc_y <= WND_RESIZE_TOP_WIDTH; + dragWhat.right = loc_x >= (mRight - mLeft) - WND_RESIZE_RIGHT_WIDTH; + dragWhat.bottom = loc_y >= (mTop - mBottom) - WND_RESIZE_BOTTOM_WIDTH; + } + // Anything to drag? + if (dragWhat) { + // Remember pos in case of dragging + lastMouseDragX = x; + lastMouseDragY = y; + } + } + break; + + case xplm_MouseUp: + io.MouseDown[button] = false; + lastMouseDragX = lastMouseDragY = -1; + dragWhat.clear(); + break; + default: + // dunno! + break; + } - switch (inMouse) { - case xplm_MouseDown: - case xplm_MouseDrag: - io.MouseDown[button] = true; - break; - case xplm_MouseUp: - io.MouseDown[button] = false; - break; - default: - // dunno! - break; - } - return 1; + return 1; } @@ -551,6 +656,54 @@ ImgWindow::onShow() return true; } +void +ImgWindow::SetWindowDragArea (int left, int top, int right, int bottom) +{ + dragLeft = left; + dragTop = top; + dragRight = right; + dragBottom = bottom; +} + +void +ImgWindow::ClearWindowDragArea () +{ + dragLeft = dragTop = dragRight = dragBottom = -1; +} + +bool +ImgWindow::HasWindowDragArea (int* pL, int* pT, + int* pR, int* pB) const +{ + // return definition if requested + if (pL) *pL = dragLeft; + if (pT) *pT = dragTop; + if (pR) *pR = dragRight; + if (pB) *pB = dragBottom; + + // is a valid drag area defined? + return + dragLeft >= 0 && dragTop >= 0 && + dragRight > dragLeft && dragBottom >= dragTop; +} + +bool +ImgWindow::IsInsideWindowDragArea (int x, int y) const +{ + // values outside the window aren't valid + if (x == -FLT_MAX || y == -FLT_MAX) + return false; + + // is a drag area defined in the first place? + if (!HasWindowDragArea()) + return false; + + // inside the defined drag area? + return + dragLeft <= x && x <= dragRight && + dragTop <= y && y <= dragBottom; +} + void ImgWindow::SafeDelete() { diff --git a/ImgWindow.h b/ImgWindow.h index ce40777..4992669 100644 --- a/ImgWindow.h +++ b/ImgWindow.h @@ -111,6 +111,9 @@ ImgWindow { /** Gets the current valid geometry (free, OS, or VR If VR, then left=bottom=0 and right=width and top=height*/ void GetCurrentWindowGeometry (int& left, int& top, int& right, int& bottom) const; + + /** Set resize limits. If set this way then the window object knows. */ + void SetWindowResizingLimits (int minW, int minH, int maxW, int maxH); /** SetVisible() makes the window visible after making the onShow() call. * It is also at this time that the window will be relocated onto the VR @@ -132,6 +135,9 @@ ImgWindow { /** Is Window in VR? */ bool IsInVR () const { return XPLMWindowIsInVR(mWindowID) != 0; } + /** Is Window inside the sim? */ + bool IsInsideSim () const { return !IsPoppedOut() && !IsInVR(); } + /** Set the positioning mode * @see https://developer.x-plane.com/sdk/XPLMDisplay/#XPLMWindowPositioningMode */ void SetWindowPositioningMode (XPLMWindowPositioningMode inPosMode, @@ -144,6 +150,36 @@ ImgWindow { /** Is Window in front of Z-order? */ bool IsWindowInFront () const { return XPLMIsWindowInFront(mWindowID) != 0; } + /** @brief Define Window drag area, ie. an area in which dragging with the mouse + * moves the entire window. + * @details Useful for windows without decoration. + * For convenience (often you want a strip at the top of the window to be the drag area, + * much like a little title bar), coordinates originate in the top-left + * corner of the window and go right/down, ie. vertical axis is contrary + * to what X-Plane usually uses, but in line with the ImGui system. + * Right/Bottom can be set much large than window size just to extend the + * drag area to the window's edges. So 0,0,INT_MAX,INT_MAX will surely + * make the entire window the drag area. + * @param left Left begin of drag area, relative to window's origin + * @param top Ditto, top begin + * @param right Ditto, right end + * @param bottom Ditto, bottom end + */ + void SetWindowDragArea (int left=0, int top=0, int right=INT_MAX, int bottom=INT_MAX); + + /** Clear the drag area, ie. stop the drag-the-window functionality */ + void ClearWindowDragArea (); + + /** Is a drag area defined? Return its sizes if wanted */ + bool HasWindowDragArea (int* pL = nullptr, int* pT = nullptr, + int* pR = nullptr, int* pB = nullptr) const; + + /** Is given position inside the defined drag area? + * @param x Horizontal position in ImGui coordinates (0,0 in top/left corner) + * @param y Vertical position in ImGui coordinates + */ + bool IsInsideWindowDragArea (int x, int y) const; + protected: /** mFirstRender can be checked during buildInterface() to see if we're * being rendered for the first time or not. This is particularly @@ -311,6 +347,43 @@ ImgWindow { int mRight; XPLMWindowLayer mPreferredLayer; + + /** Set if `xplm_WindowDecorationSelfDecoratedResizable`, + * ie. we need to handle resizing ourselves: X-Plane provides + * the "hand" mouse icon but as we catch mouse events X-Plane + * cannot handle resizing. (And passing on mouse events is + * discouraged. + * @see https://developer.x-plane.com/sdk/XPLMHandleMouseClick_f/ */ + const bool bHandleWndResize; + + /** Resize limits. There's no way to query XP, so we need to keep track ourself */ + int minWidth = 100; + int minHeight = 100; + int maxWidth = INT_MAX; + int maxHeight = INT_MAX; + + /** Window drag area in ImGui coordinates (0,0 is top/left corner) */ + int dragLeft = -1; + int dragTop = -1; + int dragRight = -1; // right > left + int dragBottom = -1; // bottom > top! + + /** Last (processed) mouse drag pos while moving/resizing */ + int lastMouseDragX = -1; + int lastMouseDragY = -1; + + /** What are we dragging right now? */ + struct DragTy { + bool wnd : 1; + bool left : 1; + bool top : 1; + bool right : 1; + bool bottom : 1; + + DragTy () { clear(); } + void clear () { wnd = left = top = right = bottom = false; } + operator bool() const { return wnd || left || top || right || bottom; } + } dragWhat; }; #endif // #ifndef IMGWINDOW_H From 1683b270b12301910bdcf61e5f9fd1ebe5a02984 Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sat, 13 Jun 2020 21:20:35 +0200 Subject: [PATCH 08/11] Fixed Linux build --- ImgWindow.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ImgWindow.h b/ImgWindow.h index 4992669..fad611a 100644 --- a/ImgWindow.h +++ b/ImgWindow.h @@ -37,6 +37,7 @@ #include "SystemGL.h" +#include #include #include From 0022d9a1e3011d77329215e61493325d840efdb4 Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sat, 13 Jun 2020 21:22:03 +0200 Subject: [PATCH 09/11] Enable compilation _without_ obsolete ImGui functionality Ie. by defining `IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in `imconfig.h` --- ImgWindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ImgWindow.cpp b/ImgWindow.cpp index d6a90b9..8ec4067 100644 --- a/ImgWindow.cpp +++ b/ImgWindow.cpp @@ -82,8 +82,10 @@ ImgWindow::ImgWindow( first_init=true; } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // we render ourselves, we don't use the DrawListsFunc io.RenderDrawListsFn = nullptr; +#endif // set up the Keymap io.KeyMap[ImGuiKey_Tab] = XPLM_VK_TAB; io.KeyMap[ImGuiKey_LeftArrow] = XPLM_VK_LEFT; From 05a763356f2a1a22256c577edbeb1076f70f7d3e Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sun, 28 Jun 2020 01:28:52 +0200 Subject: [PATCH 10/11] Allow more characters to pass on to ImGui --- ImgWindow.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ImgWindow.cpp b/ImgWindow.cpp index 8ec4067..935c05a 100644 --- a/ImgWindow.cpp +++ b/ImgWindow.cpp @@ -252,7 +252,9 @@ ImgWindow::RenderImGui(ImDrawData *draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) ImGuiIO& io = ImGui::GetIO(); - draw_data->ScaleClipRects(io.DisplayFramebufferScale); + if (io.DisplayFramebufferScale.x != 1.0 || + io.DisplayFramebufferScale.y != 1.0) + draw_data->ScaleClipRects(io.DisplayFramebufferScale); updateMatrices(); @@ -541,10 +543,11 @@ ImgWindow::HandleKeyFuncCB( io.KeyAlt = (inFlags & xplm_OptionAltFlag) == xplm_OptionAltFlag; io.KeyCtrl = (inFlags & xplm_ControlFlag) == xplm_ControlFlag; - if ((inFlags & xplm_DownFlag) == xplm_DownFlag - && !io.KeyCtrl - && !io.KeyAlt - && isprint(inKey)) { + // inKey will only includes printable characters, + // but also those created with key combinations like @ or {} + if ((inFlags & xplm_DownFlag) == xplm_DownFlag && + inKey > '\0') + { char smallStr[2] = { inKey, 0 }; io.AddInputCharactersUTF8(smallStr); } From 6e9090e19f6120230a37bfe98e0c088bedfe0d0b Mon Sep 17 00:00:00 2001 From: TwinFan Date: Fri, 22 Jan 2021 00:01:18 +0100 Subject: [PATCH 11/11] Handle backspace in VR / handle loosing focus by sending [Esc] --- ImgWindow.cpp | 75 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/ImgWindow.cpp b/ImgWindow.cpp index 935c05a..4c64e1c 100644 --- a/ImgWindow.cpp +++ b/ImgWindow.cpp @@ -48,6 +48,7 @@ static XPLMDataRef gVrEnabledRef = nullptr; static XPLMDataRef gModelviewMatrixRef = nullptr; static XPLMDataRef gViewportRef = nullptr; static XPLMDataRef gProjectionMatrixRef = nullptr; +static XPLMDataRef gFrameRatePeriodRef = nullptr; std::shared_ptr ImgWindow::sFontAtlas; @@ -78,7 +79,7 @@ ImgWindow::ImgWindow( gModelviewMatrixRef = XPLMFindDataRef("sim/graphics/view/modelview_matrix"); gViewportRef = XPLMFindDataRef("sim/graphics/view/viewport"); gProjectionMatrixRef = XPLMFindDataRef("sim/graphics/view/projection_matrix"); - + gFrameRatePeriodRef = XPLMFindDataRef("sim/operation/misc/frame_rate_period"); first_init=true; } @@ -118,7 +119,7 @@ ImgWindow::ImgWindow( if (mFontAtlas) { mFontTexture = static_cast(reinterpret_cast(io.Fonts->TexID)); } else { - if (iFontAtlas->TexID == nullptr) { + if (!iFontAtlas || iFontAtlas->TexID == nullptr) { // fallback binding if an atlas wasn't explicitly set. unsigned char *pixels; int width, height; @@ -130,7 +131,7 @@ ImgWindow::ImgWindow( mFontTexture = (GLuint)texNum; // upload texture. - XPLMBindTexture2d(mFontTexture, 0); + XPLMBindTexture2d((int)mFontTexture, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -256,7 +257,7 @@ ImgWindow::RenderImGui(ImDrawData *draw_data) io.DisplayFramebufferScale.y != 1.0) draw_data->ScaleClipRects(io.DisplayFramebufferScale); - updateMatrices(); + updateMatrices(); // We are using the OpenGL fixed pipeline because messing with the // shader-state in X-Plane is not very well documented, but using the fixed @@ -294,7 +295,7 @@ ImgWindow::RenderImGui(ImDrawData *draw_data) if (pcmd->UserCallback) { pcmd->UserCallback(cmd_list, pcmd); } else { - XPLMBindTexture2d((GLuint)(intptr_t)pcmd->TextureId, 0); + XPLMBindTexture2d((int)(intptr_t)pcmd->TextureId, 0); // Scissors work in viewport space - must translate the coordinates from ImGui -> Boxels, then Boxels -> Native. //FIXME: it must be possible to apply the scale+transform manually to the projection matrix so we don't need to doublestep. @@ -358,6 +359,11 @@ ImgWindow::updateImgui() float win_width = static_cast(mRight - mLeft); float win_height = static_cast(mTop - mBottom); + // Needed to add this to prevent io.DeltaTime causing a CTD because when X-Plane starts FrameRatePeriod is equal to 0.0f + float FrameRatePeriod = XPLMGetDataf(gFrameRatePeriodRef); + if (FrameRatePeriod > 0.0f) { + io.DeltaTime = XPLMGetDataf(gFrameRatePeriodRef); + } io.DisplaySize = ImVec2(win_width, win_height); // in boxels, we're always scale 1, 1. io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); @@ -400,6 +406,13 @@ ImgWindow::DrawWindowCB(XPLMWindowID /* inWindowID */, void *inRefcon) // Give subclasses a chance to do something after all rendering thisWindow->afterRendering(); + + // Hack: Reset the Backspace key if in VR (see HandleKeyFuncCB for details) + if (thisWindow->bResetBackspace) { + ImGuiIO& io = ImGui::GetIO(); + io.KeysDown[XPLM_VK_BACK] = false; + thisWindow->bResetBackspace = false; + } } int @@ -531,26 +544,50 @@ ImgWindow::HandleKeyFuncCB( XPLMKeyFlags inFlags, char inVirtualKey, void * inRefcon, - int /*losingFocus*/) + int blosingFocus) { auto *thisWindow = reinterpret_cast(inRefcon); ImGui::SetCurrentContext(thisWindow->mImGuiContext); ImGuiIO& io = ImGui::GetIO(); if (io.WantCaptureKeyboard) { - auto vk = static_cast(inVirtualKey); - io.KeysDown[vk] = (inFlags & xplm_DownFlag) == xplm_DownFlag; - io.KeyShift = (inFlags & xplm_ShiftFlag) == xplm_ShiftFlag; - io.KeyAlt = (inFlags & xplm_OptionAltFlag) == xplm_OptionAltFlag; - io.KeyCtrl = (inFlags & xplm_ControlFlag) == xplm_ControlFlag; - - // inKey will only includes printable characters, - // but also those created with key combinations like @ or {} - if ((inFlags & xplm_DownFlag) == xplm_DownFlag && - inKey > '\0') + + // Loosing focus? That's not exactly something ImGui allows us to do... + // we try convincing ImGui to let it go by sending an [Esc] key + if (blosingFocus) { + io.KeysDown[int(XPLM_VK_ESCAPE)] = true; + } + else { - char smallStr[2] = { inKey, 0 }; - io.AddInputCharactersUTF8(smallStr); - } + // Hack for the Backspace key in VR: + // Apparently, the virtual VR keyboard sends both the Up and the Down + // event within the same drawing cycle, which would overwrite + // io.KeyDown[XPLM_VK_BACK] with false again before we could pass on true. + // Also see https://forums.x-plane.org/index.php?/forums/topic/147139-dear-imgui-x-plane/&do=findComment&comment=2032062 + // though I am following a different solution: + // So we ignore the "up" event (release key) here, and do the actual + // release only after the next drawing cycle (flag bResetBackspace). + // (And this little delay doesn't hurt in non-VR either, so we don't even test for VR.) + + // If Backspace is _released_ ... + if (inVirtualKey == XPLM_VK_BACK && !(inFlags & xplm_DownFlag)) { + thisWindow->bResetBackspace = true; // have it reset only later in DrawWindowCB + } + else + // in all normal cases: save the up/down flag as it comes from XP + io.KeysDown[int(inVirtualKey)] = (inFlags & xplm_DownFlag) == xplm_DownFlag; + io.KeyShift = (inFlags & xplm_ShiftFlag) == xplm_ShiftFlag; + io.KeyAlt = (inFlags & xplm_OptionAltFlag) == xplm_OptionAltFlag; + io.KeyCtrl = (inFlags & xplm_ControlFlag) == xplm_ControlFlag; + + // inKey will only includes printable characters, + // but also those created with key combinations like @ or {} + if ((inFlags & xplm_DownFlag) == xplm_DownFlag && + inKey > '\0') + { + char smallStr[2] = { inKey, 0 }; + io.AddInputCharactersUTF8(smallStr); + } + } } }