Cannot drag panels to other monitor with other DPI |
Post Reply |
Author | |
andredevries
Newbie Joined: 22 June 2018 Location: Netherlands Status: Offline Points: 5 |
Post Options
Thanks(0)
Posted: 22 June 2018 at 8:55am |
Multiple 4K screens, some with 100% scaling, others with scaling other than 100. Can't move any of the windows across to the other screens if the scaling is different. They get stuck; they jump to a different location on the screen it comes from. See this short video showing the issue: https://www.youtube.com/watch?v=IFiAtYSNl30 This happens when the "main" monitor is to the right of the "second" monitor, so the second monitor will have negative horizontal co-ordinates. Now, if the two monitors have different DPI scale settings then none of our "Floating Tool Panel" windows, that are based on a CodeJock control for dockable floating panels, can be dragged into the left panel (i.e the second monitor with negative co-ordinates). Note: When you drag the floating panel towards the left monitor, Windows will send a notification to it when more than 50% of the panel is in the left monitor (WM_DPICHANGED). The notification is to tell the panel that it is now dragged into a monitor that has a different DPI Scale. It seems that the CodeJock Docking panel should respond to this notification in some way and it seems it is not doing that, or incorrectly? I reproduced the issue using Codejock MFC 18.3.0. I hope this problem can be solved or someone can tell me how to fix it myself. Thanks. |
|
olebed
Admin Group Joined: 01 July 2014 Location: Ukraine Status: Offline Points: 841 |
Post Options
Thanks(0)
|
Hello, I think this problem related to new macros XTP_POINT_FROM_LPARAM It constructs wrong CPoint if .x or .y from lparam are negative. I have investigated that negative points are only in method CXTPCommandBarsFrameHook::OnHookMessage() So as workaround you can delete this macros from CXTPCommandBarsFrameHook::OnHookMessage because CPoint has own constructor with lparam as parameter CPoint(LPARAM dwPoint) throw(); CPoint point = XTP_POINT_FROM_LPARAM(lParam); > CPoint point(lParam); Regards, Oleksandr Lebed
|
|
olebed
Admin Group Joined: 01 July 2014 Location: Ukraine Status: Offline Points: 841 |
Post Options
Thanks(0)
|
However if you say that bug with DockingPane control, than I can say that bug the same as with XTP_POINT_FROM_LPARAM - signed/unsigned casting.
|
|
Sven
Senior Member Joined: 21 August 2003 Location: Germany Status: Offline Points: 127 |
Post Options
Thanks(0)
|
I have changed XTP_POINT_FROM_LPARAM to
#define XTP_POINT_FROM_LPARAM(lParam) CPoint(static_cast<short>(LOWORD(lParam)), static_cast<short>(HIWORD(lParam))) The original non-typecast macro is also the reason, that buttons in themed frames (minimize, maximize, restore) can't be clicked if the main window is on the left monitor with negative coordinates. |
|
olebed
Admin Group Joined: 01 July 2014 Location: Ukraine Status: Offline Points: 841 |
Post Options
Thanks(0)
|
Sven, yes hit test for frame buttons doesn't work due to signed-unsigned cast. But CPoint class has own constructor with initialization with LParam.
|
|
andredevries
Newbie Joined: 22 June 2018 Location: Netherlands Status: Offline Points: 5 |
Post Options
Thanks(0)
|
Thanks for your responses. However, I still have difficulty to come to a solution. It sounds plausible that it may be a signed/unsigned casting issue, and it might be a conversion issue between LPARAM and Point. But when I search in the sources on XTP_POINT_FROM_LPARAM, I find nothing. Where is that macro defined? I could not find anything similar that may be a problem. Any more ideas? |
|
olebed
Admin Group Joined: 01 July 2014 Location: Ukraine Status: Offline Points: 841 |
Post Options
Thanks(0)
|
XTP_POINT_FROM_LPARAM introduced in v18.4.
Unfortunately I can't reproduce your problem. Can you provide sample ? I have monitors configuration of work station as you described except 4k (only fullHD). |
|
andredevries
Newbie Joined: 22 June 2018 Location: Netherlands Status: Offline Points: 5 |
Post Options
Thanks(0)
|
Aha, I am using v18.3. I just tested the DockingPane samples, they work fine. Seems like I am doing something wrong here. Unfortunately it is not so easy to isolate sources to provide an example (and I am not that familiar with it). Could you tell me what gets triggered when moving a docking pane to a screen with other DPI?
|
|
olebed
Admin Group Joined: 01 July 2014 Location: Ukraine Status: Offline Points: 841 |
Post Options
Thanks(0)
|
see method CXTPDockingPaneMiniWnd::UpdatePosition() also for drawing used ToolkitPro1840UD.dll!CXTPDockingPaneOffice2013Theme::DrawFloatingFrame(CDC * pDC, CXTPDockingPaneMiniWnd * pPane, CRect rc) Line 298 C++ ToolkitPro1840UD.dll!CXTPDockingPaneMiniWnd::OnNcPaint() Line 699 C++
|
|
andredevries
Newbie Joined: 22 June 2018 Location: Netherlands Status: Offline Points: 5 |
Post Options
Thanks(0)
|
Thanks, but does not help so far. Debugger does not stop at any of those methods. Will have to search further myself I guess. Thanks anyway.
|
|
Cedric_ar
Groupie Joined: 04 September 2012 Status: Offline Points: 19 |
Post Options
Thanks(0)
|
hi,
I have the same issue. it's realy hard to switch pane to a different monitor with a different DPI. I have a monitor in 125% and another in 100%. I try to remove XTPWM_DOCKINGPANE_NOTIFY to test if the problem is on my side, but i have the same issue. the mainframe inherited from class CMainFrame:public CXTPMDIFrameWnd, CXTPCommandBarsFrameHook i initialize the dockingpane manager like this kPaneManager = new CXTPDockingPaneManager(); kPaneManager->InstallDockingPanes( AfxGetApp()->m_pMainWnd ); kPaneManager->SetTheme( /*xtpPaneThemeVisualStudio2005Beta1*/xtpPaneThemeVisualStudio2015 ); kPaneManager->SetAlphaDockingContext(TRUE); kPaneManager->SetShowDockingContextStickers(/*TRUE*/(XTPDockingContextStickerStyle)xtpPaneStickerStyleVisualStudio2015Dark); i'm on 18.5.0.
|
|
rdhd
Senior Member Joined: 13 August 2007 Location: United States Status: Offline Points: 891 |
Post Options
Thanks(0)
|
olebed, is this the issue I mentioned here: http://forum.codejock.com/forum_posts.asp?TID=23877&title=docking-pane-drag-is-wrong-with-this-setup I have the same problem. Here is my setup. I have 3 monitors. A 2k one on the left. My middle monitor is my main monitor, a 4k monitor scaled 200%. My third monitor is a 2k monitor to the right of my main monitor. I can drag a pane from the main monitor to the left one. But when I cross the boundary, the pane "separates" from the cursor. The closer to the top of the monitor I am, the closer the blue window being dragged is to the cursor. Move down and it separates at a 2X rate. Now move towards the right monitor. When it reaches the halfway point, it quits moving. That's because CJ thinks the cursor is off the desktop. The virtual size of the monitor does not match the physical size on the two 2k monitors. GetDeviceCaps and GetMonitorInfoEx shows the difference and also clues on in on how to offset and scale the window rect calculations.
|
|
rdhd
Senior Member Joined: 13 August 2007 Location: United States Status: Offline Points: 891 |
Post Options
Thanks(0)
|
So, what is the fix here? Even with 19.0 I still have the problem. We really could use a fix.
|
|
rdhd
Senior Member Joined: 13 August 2007 Location: United States Status: Offline Points: 891 |
Post Options
Thanks(0)
|
andredevries and oled, I can guess why one might not be able to duplicate the issue. If an application is built with per monitor aware DPI settings (usually set in the manifest), the issue will not appear regardless of the monitor setup, including 4k > 100%). If built with system aware DPI setting, the issue easily shows up. I have not tried "none" for the DPI setting in the VS Manifest setting. I'm not where my 4k monitor is or I would try setting the settings on a sample. For our app, I have removed the DPI setting via manifest and instead set it programmatically after reading a registry setting I use to determine which of the DPI settings I want to try. I would not be surprised if the OS version also has an effect. My 4k setup has a box with OS Version 1803. I know Microsoft has made some changes in 1903 and put out a hot fix for that OS and supposedly has rolled the change into the latest updates. That fix had to do with resizing a window where the dynamic rectangle could fail to show up on the correct monitor and could fail to show up anywhere on the desktop when not using per monitor aware.
|
|
rdhd
Senior Member Joined: 13 August 2007 Location: United States Status: Offline Points: 891 |
Post Options
Thanks(0)
|
Don't know if this helps but MFC has an issue drawing a frame rect around a toolbar depending on the monitors involved and I obtained the following code that showed one way to fix it. But, we don't build MFC and we found setting per monitor aware allows an MFC app with a toolbar to work just fine. In any case, here is some code that simulates a resizing rectangle that worked on my desktop with 3 monitors including the 4k set to 200%. The magic is in VirtualToPhysical. I'd upload the project but don't see an easy way to do so. It is a single source file though. // DrawToScreenSample.cpp : Defines the entry point for the application.
// #include "stdafx.h" #include <windowsx.h> #include "DrawToScreenSample.h" #define MAX_LOADSTRING 100 // Global Variables: HINSTANCE hInst; // current instance WCHAR szTitle[MAX_LOADSTRING]; // The title bar text WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name const WCHAR dummyMoveWindowClass[] = L"DummyMoveWindow"; using SetThreadDpiAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*)(_In_ DPI_AWARENESS_CONTEXT dpiContext); SetThreadDpiAwarenessContextFunc g_setThreadDpiAwarenessContext; HWND g_windowToTrack; bool m_movingWindow; RECT g_lastRect; POINT g_moveStartPoint; POINT g_moveEndPoint; bool g_moveWindow; HWND g_dummyWindowToMove; // Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); ATOM RegisterDummyMoveWindowClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void DrawToScreen(HWND selectedWindow); RECT VirtualToPhysical(const RECT &virtualRect); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // Initialize global strings LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_DRAWTOSCREENSAMPLE, szWindowClass, MAX_LOADSTRING); if (!MyRegisterClass(hInstance)) { return FALSE; } // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } HMODULE user32Dll = GetModuleHandle(L"User32.dll"); if (!user32Dll) { return FALSE; } g_setThreadDpiAwarenessContext = reinterpret_cast<SetThreadDpiAwarenessContextFunc>(GetProcAddress(user32Dll, "SetThreadDpiAwarenessContext")); if (!g_setThreadDpiAwarenessContext && GetLastError() != ERROR_PROC_NOT_FOUND) { return FALSE; } if (g_setThreadDpiAwarenessContext && !RegisterDummyMoveWindowClass(hInstance)) { return FALSE; } //g_setThreadDpiAwarenessContext = nullptr; HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DRAWTOSCREENSAMPLE)); MSG msg; // Main message loop: while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return static_cast<int>(msg.wParam); } // // FUNCTION: MyRegisterClass() // // PURPOSE: Registers the window class. // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DRAWTOSCREENSAMPLE)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_DRAWTOSCREENSAMPLE); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassExW(&wcex); } ATOM RegisterDummyMoveWindowClass(HINSTANCE hInstance) { WNDCLASSEXW wcex = {}; wcex.hInstance = hInstance; wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpfnWndProc = []( _In_ HWND hWnd, _In_ UINT message, _In_ WPARAM wParam, _In_ LPARAM lParam) ->LRESULT { switch (message) { case WM_DPICHANGED: { RECT* const prcNewWindow = reinterpret_cast<RECT*>(lParam); SetWindowPos(hWnd, nullptr, prcNewWindow->left, prcNewWindow->top, prcNewWindow->right - prcNewWindow->left, prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE); break; } } return DefWindowProc(hWnd, message, wParam, lParam); }; wcex.lpszClassName = dummyMoveWindowClass; wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1); return RegisterClassExW(&wcex); } // // FUNCTION: InitInstance(HINSTANCE, int) // // PURPOSE: Saves instance handle and creates main window // // COMMENTS: // // In this function, we save the instance handle in a global variable and // create and display the main program window. // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // Store instance handle in our global variable HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) // // PURPOSE: Processes messages for the main window. // // WM_COMMAND - process the application menu // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_LBUTTONDOWN: SetCapture(hWnd); return DefWindowProc(hWnd, message, wParam, lParam); case WM_MOUSEMOVE: { DPI_AWARENESS_CONTEXT oldAwareness = nullptr; if (g_setThreadDpiAwarenessContext) { oldAwareness = g_setThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); } if (GetCapture() == hWnd) { DWORD postion = GetMessagePos(); POINT pt; pt.x = GET_X_LPARAM(postion); pt.y = GET_Y_LPARAM(postion); if (!m_movingWindow) { HWND window = WindowFromPoint(pt); HWND topLevelWindow = GetAncestor(window, GA_ROOT); if ((g_windowToTrack || topLevelWindow) && (g_windowToTrack != topLevelWindow)) { DrawToScreen(topLevelWindow); g_windowToTrack = topLevelWindow; } // if the user wants to move the top-level window if (topLevelWindow && (wParam & MK_CONTROL)) { m_movingWindow = true; g_moveStartPoint = pt; g_moveWindow = false; if (g_setThreadDpiAwarenessContext) { RECT physicalRect; if (GetWindowRect(g_windowToTrack, &physicalRect)) { g_dummyWindowToMove = CreateWindow(dummyMoveWindowClass, nullptr, WS_POPUP/* | WS_VISIBLE*/, physicalRect.left, physicalRect.top, physicalRect.right - physicalRect.left, physicalRect.bottom - physicalRect.top, nullptr, nullptr, hInst, nullptr); } } } } else { if (!(wParam & MK_CONTROL)) { ReleaseCapture(); } else { DrawToScreen(nullptr); } } } if (g_setThreadDpiAwarenessContext) { g_setThreadDpiAwarenessContext(oldAwareness); } } return DefWindowProc(hWnd, message, wParam, lParam); case WM_LBUTTONUP: { if (m_movingWindow) { if (wParam & MK_CONTROL) { DPI_AWARENESS_CONTEXT oldAwareness = nullptr; if (g_setThreadDpiAwarenessContext) { oldAwareness = g_setThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); } DrawToScreen(nullptr); if (g_setThreadDpiAwarenessContext) { g_setThreadDpiAwarenessContext(oldAwareness); } DWORD postion = GetMessagePos(); g_moveEndPoint.x = GET_X_LPARAM(postion); g_moveEndPoint.y = GET_Y_LPARAM(postion); g_moveWindow = true; } } ReleaseCapture(); } return DefWindowProc(hWnd, message, wParam, lParam); case WM_CAPTURECHANGED: if (m_movingWindow) { m_movingWindow = false; DrawToScreen(nullptr); if (g_moveWindow) { RECT virtualRect; if (g_dummyWindowToMove) { if (GetWindowRect(g_dummyWindowToMove, &virtualRect)) { SetWindowPos(g_windowToTrack, nullptr, virtualRect.left, virtualRect.top, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE); } } else { if (GetWindowRect(g_windowToTrack, &virtualRect)) { OffsetRect(&virtualRect, (g_moveEndPoint.x - g_moveStartPoint.x), (g_moveEndPoint.y - g_moveStartPoint.y)); SetWindowPos(g_windowToTrack, nullptr, virtualRect.left, virtualRect.top, 0, 0, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE); } } } if (g_dummyWindowToMove) { DestroyWindow(g_dummyWindowToMove); g_dummyWindowToMove = nullptr; } g_windowToTrack = nullptr; } else if (g_windowToTrack) { DrawToScreen(nullptr); g_windowToTrack = nullptr; } return DefWindowProc(hWnd, message, wParam, lParam); case WM_COMMAND: { int wmId = LOWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: Add any drawing code that uses hdc here... EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } void DrawToScreen(HWND selectedWindow) { HDC dc = GetDC(nullptr); if (dc) { if (SetROP2(dc, R2_XORPEN)) { WORD grayPattern[8]; for (int i = 0; i < 8; i++) { grayPattern = WORD(0x5555 << (i & 1)); } HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern); if (grayBitmap) { LOGBRUSH logBrush = {}; logBrush.lbStyle = BS_PATTERN; logBrush.lbHatch = reinterpret_cast<LONG_PTR>(grayBitmap); HPEN pen = ExtCreatePen(PS_GEOMETRIC, 0, &logBrush, 0, nullptr); if (pen) { HGDIOBJ oldPen = SelectObject(dc, pen); if (oldPen) { HGDIOBJ oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); if (oldBrush) { if (g_windowToTrack) { Rectangle(dc, g_lastRect.left, g_lastRect.top, g_lastRect.right, g_lastRect.bottom); } RECT calculatedPhysicalRect = {}; if (m_movingWindow) { DWORD postion = GetMessagePos(); POINT pt; pt.x = GET_X_LPARAM(postion); pt.y = GET_Y_LPARAM(postion); RECT virtualRect; if (GetWindowRect(g_windowToTrack, &virtualRect)) { OffsetRect(&virtualRect, (pt.x - g_moveStartPoint.x), (pt.y - g_moveStartPoint.y)); if (g_dummyWindowToMove) { if (SetWindowPos(g_dummyWindowToMove, nullptr, virtualRect.left, virtualRect.top, virtualRect.right - virtualRect.left, virtualRect.bottom - virtualRect.top, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE)) { if (!GetWindowRect(g_dummyWindowToMove, &calculatedPhysicalRect)) { SetRectEmpty(&calculatedPhysicalRect); } } } else { calculatedPhysicalRect = VirtualToPhysical(virtualRect); } } } else if (selectedWindow) { if (g_setThreadDpiAwarenessContext) { if (!GetWindowRect(selectedWindow, &calculatedPhysicalRect)) { SetRectEmpty(&calculatedPhysicalRect); } } else { RECT virtualRect; if (GetWindowRect(selectedWindow, &virtualRect)) { calculatedPhysicalRect = VirtualToPhysical(virtualRect); } } } if (!IsRectEmpty(&calculatedPhysicalRect)) { if (Rectangle(dc, calculatedPhysicalRect.left, calculatedPhysicalRect.top, calculatedPhysicalRect.right, calculatedPhysicalRect.bottom)) { g_lastRect = calculatedPhysicalRect; } } SelectObject(dc, oldBrush); } SelectObject(dc, oldPen); } DeleteObject(pen); } DeleteObject(grayBitmap); } } ReleaseDC(nullptr, dc); } } RECT VirtualToPhysical(const RECT &virtualRect) { RECT calculatedPhysicalRect = {}; POINT centerPoint; centerPoint.x = virtualRect.left + (virtualRect.right - virtualRect.left) / 2; centerPoint.y = virtualRect.top + (virtualRect.bottom - virtualRect.top) / 2; HMONITOR monitor = MonitorFromPoint(centerPoint, MONITOR_DEFAULTTONEAREST); if (monitor) { MONITORINFOEX monitorInfo = {}; monitorInfo.cbSize = sizeof(MONITORINFOEX); if (GetMonitorInfo(monitor, &monitorInfo)) { HDC monitorDC = CreateDC(nullptr, monitorInfo.szDevice, nullptr, nullptr); if (monitorDC) { int screenWidth = GetDeviceCaps(monitorDC, DESKTOPHORZRES); int virtualizedScreenWidth = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; calculatedPhysicalRect.left = (virtualRect.left - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.left; calculatedPhysicalRect.top = (virtualRect.top - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.top; calculatedPhysicalRect.right = (virtualRect.right - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.left; calculatedPhysicalRect.bottom = (virtualRect.bottom - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.top; DeleteDC(monitorDC); } } } return calculatedPhysicalRect; } //void DrawMoving() //{ // HDC dc = GetDC(nullptr); // // SetROP2(dc, R2_XORPEN); // // WORD grayPattern[8]; // for (int i = 0; i < 8; i++) // { // grayPattern = (WORD)(0x5555 << (i & 1)); // } // // HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern); // // LOGBRUSH logBrush = {}; // logBrush.lbStyle = BS_PATTERN; // logBrush.lbHatch = (LONG_PTR)grayBitmap; // HPEN pen = ExtCreatePen(PS_GEOMETRIC, 0, &logBrush, 0, nullptr); // // SelectObject(dc, pen); // SelectObject(dc, GetStockObject(NULL_BRUSH)); // // if (g_windowToTrack) // { // Rectangle(dc, g_lastRect.left, g_lastRect.top, g_lastRect.right, g_lastRect.bottom); // } // // if (m_movingWindow) // // { // DWORD postion = GetMessagePos(); // // POINT pt; // // pt.x = GET_X_LPARAM(postion); // // pt.y = GET_Y_LPARAM(postion); // // // RECT virtualRect; // GetWindowRect(g_windowToTrack, &virtualRect); // OffsetRect(&virtualRect, (pt.x - g_moveStartPoint.x), (pt.y - g_moveStartPoint.y)); // // // RECT calculatedPhysicalRect = {}; // // if (g_dummyWindowToMove) // // { // SetWindowPos(g_dummyWindowToMove, nullptr, virtualRect.left, virtualRect.top, 0, 0, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE); // // // DPI_AWARENESS_CONTEXT oldAwareness = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); // GetWindowRect(g_dummyWindowToMove, &calculatedPhysicalRect); // // SetThreadDpiAwarenessContext(oldAwareness); // } // else // { // POINT centerPoint; // centerPoint.x = virtualRect.left + (virtualRect.right - virtualRect.left) / 2; // centerPoint.y = virtualRect.top + (virtualRect.bottom - virtualRect.top) / 2; // HMONITOR monitor = MonitorFromPoint(centerPoint, MONITOR_DEFAULTTONEAREST); // MONITORINFOEX monitorInfo = {}; // monitorInfo.cbSize = sizeof(MONITORINFOEX); // GetMonitorInfo(monitor, &monitorInfo); // // HDC monitorDC = CreateDC(nullptr, monitorInfo.szDevice, nullptr, nullptr); // int screenWidth = GetDeviceCaps(monitorDC, DESKTOPHORZRES); // int virtualizedScreenWidth = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; // // calculatedPhysicalRect.left = ((virtualRect.left - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.left; // calculatedPhysicalRect.top = ((virtualRect.top - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.top; // calculatedPhysicalRect.right = ((virtualRect.right - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.left; // calculatedPhysicalRect.bottom = ((virtualRect.bottom - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.top; // } // // Rectangle(dc, calculatedPhysicalRect.left, calculatedPhysicalRect.top, calculatedPhysicalRect.right, calculatedPhysicalRect.bottom); // // g_lastRect = calculatedPhysicalRect; // } // // DeleteObject(pen); // DeleteObject(grayBitmap); // // ReleaseDC(nullptr, dc); //} // Message handler for about box. INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; } |
|
rdhd
Senior Member Joined: 13 August 2007 Location: United States Status: Offline Points: 891 |
Post Options
Thanks(0)
|
Any fix at all? Running with per monitor aware causes the windows (frame and others) to scale way up on my non-4k monitors when my 4k is primary and is at 200%. If the primary is not the 4k and isn't scaled and the 4k is scaled, there are issues with that desktop topology too.
|
|
Fredrik
Senior Member Joined: 22 June 2005 Status: Offline Points: 235 |
Post Options
Thanks(0)
|
I just checked with the 20.2 release, but I still cannot drag docking panes to any of my secondary windows
|
|
Windows 10, Visual Studio 20157, Toolkit Pro 18.3.0
|
|
rdhd
Senior Member Joined: 13 August 2007 Location: United States Status: Offline Points: 891 |
Post Options
Thanks(0)
|
I have found that CXTPDockingPaneContext::Move is calling XTPMultiMonitor()->GetWorkArea. This call is what results in getting "stuck" crossing monitors. I use the debugger to bypass that code and always set m_rectDragFrame to m_rectDragFrameScreen. Now, I can cross monitor boundaries. The window "sticks" on the monitor boundary because the rect isn't being updated and m_rectLast never changes. This prevents the call to SetWindowPos from occurring. I have three monitors with my 4k at 175% being the main monitor with a non-4k to the left and another to the right. When I change that code I also see the following: If I move the window when it is on my main monitor, I have to drag slowly. As long as I do, the drag point and the cursor position are close together and the window moves. If I move too quickly and the cursor gets outside the drag window, the drag window jumps as ptOffset changes radically. I am monitoring the point GetMessagePos returns and I see it is always the same as the message.pt obtained in Track. But, as soon as I move quickly, so the cursor gets "ahead" of the window being moved, the coordinates I am getting when on my main (4k) monitor change - they are no longer scaled by my DPI scale factor. Oh, and this hooker talk in this thread - when I break into the Track code, no hooker is on the call stack. So, is that really an issue? Once the cursor and the window gets separated, I can see the window pos and the cursor converging if I move towards the left size of my main monitor (x == 0) and I see it diverge more an more as I move to the right side of that monitor due to the point not being scaled. I have not detected why the message point changes when the window pos doesn't keep up with the cursor pos. And, when they two do separate, if I move the cursor to either the left or right monitor, the window pos syncs back up to the cursor pos and movement is fine on those monitors. Right now, I think this monitor work area intersection test is just plain wrong. In the very least, the code needs to enumerate each monitor and check for intersection with each one. Now, if I can figure out why the message point x and y values seem to depend on whether the actual cursor is "on" the floating pane or not, then I expect the tracking on a system aware DPI setting will work as long as I fix the "intersect with monitor" code.
|
|
rdhd
Senior Member Joined: 13 August 2007 Location: United States Status: Offline Points: 891 |
Post Options
Thanks(0)
|
I left out some important info! In order to even get it to start moving correct when on the main/4k/scaled monitor, I also have to set the DPM's m_bShowContentsWhileDragging to true. That way, the window actually moves with the cursor. Otherwise, the layered "move context window" moves and as soon as the cursor gets out of the actual pane's window area, I get the "jump" as the message point is no longer scaled.
|
|
rdhd
Senior Member Joined: 13 August 2007 Location: United States Status: Offline Points: 891 |
Post Options
Thanks(0)
|
So once the code to fix the issue with monitor boundaries is fixed (or eliminated), I can move across the monitor. But, if I move "fast" on the scaled monitor so the cursor gets off the window being dragged, including the blue "context" window, the OS no longer scales the mouse point. So, here's the fix for that - Add WS_EX_NOACTIVATE to the created context window: void CXTPDockingPaneContext::CreateContextWindow(CXTPDockingPaneContextAlphaWnd* pWnd) { ASSERT(m_bUseAlphaContext); if (pWnd->GetSafeHwnd()) return; pWnd->CreateEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE, AfxRegisterWndClass(NULL, AfxGetApp()->LoadStandardCursor(IDC_ARROW)), 0, WS_POPUP, CRect(0, 0, 0, 0), nullptr/*m_pManager->GetSite()*/, 0); if (m_pfnSetLayeredWindowAttributes) { ((PFNSETLAYEREDWINDOWATTRIBUTES)m_pfnSetLayeredWindowAttributes)(pWnd->m_hWnd, 0, 100, LWA_ALPHA); } } Once you do that, you can drag across monitors and the context window doesn't separate from the cursor and jump (due to the instant change from scaled points to non-scaled points. If you are calling SetShowContentsWhileDragging(TRUE) so the blue context window doesn't show up, the issue with the scaling of the message point will still occur unless you add WS_EX_NOACTIVATE to the docking pane mini window (or some dialog in it). Once I did that to my floating panes and fixed the monitor boundary issue, I can now drag floating panes with the DPI awareness context set to system aware. I'm not sure I'm comfortable setting that style on all our floating panes as no telling what regression I might end up with. But, it is perfectly safe to do so on the context window. I'm thinking I might check for that style when starting the drag and if it is not there, add it and remove it when the drag is done. In any case, so far my testing is showing me these two issues are fixed for me (monitor boundary sticking and window jumping if the cursor moves too fast). What I also plan to do is modify the CJ code I commented out in CXTPDockingPaneContext::Move. Instead of not calling XTPMultiMonitor()->GetWorkArea() and intersecting the rect of the window being moved with it, just enum the monitors and intersect with the work area of each. That too will take a bit of work as the monitor info does NOT return scaled sizes. They are physical so a physical to virtual transform will be needed. Or perhaps just cheat a bit and call this to get the rect: void GetDesktopRectForMultipleMonitors(LPRECT pDesktopRect) { if(pDesktopRect) { pDesktopRect->left = GetSystemMetrics(SM_XVIRTUALSCREEN); pDesktopRect->top = GetSystemMetrics(SM_YVIRTUALSCREEN); int w = GetSystemMetrics(SM_CXVIRTUALSCREEN); pDesktopRect->right = pDesktopRect->left + w; int h = GetSystemMetrics(SM_CYVIRTUALSCREEN); pDesktopRect->bottom = pDesktopRect->top + h; } } Then intersect with that rect.
|
|
Post Reply | |
Tweet
|
Forum Jump | Forum Permissions You cannot post new topics in this forum You cannot reply to topics in this forum You cannot delete your posts in this forum You cannot edit your posts in this forum You cannot create polls in this forum You cannot vote in polls in this forum |