winemac: When dragging an undecorated window, keep the title bar vertically within the work area.

In particular, keep the title bar from moving behind the menu bar or Dock (when
it's on the bottom).
This commit is contained in:
Ken Thomases 2013-12-16 17:23:07 -06:00 committed by Alexandre Julliard
parent b423532f94
commit b877885400
2 changed files with 225 additions and 1 deletions

View File

@ -177,6 +177,7 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
extern void macdrv_mouse_moved(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN;
extern void macdrv_mouse_scroll(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN;
extern void macdrv_release_capture(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN;
extern void CDECL macdrv_SetCapture(HWND hwnd, UINT flags) DECLSPEC_HIDDEN;
extern void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data) DECLSPEC_HIDDEN;
extern void macdrv_keyboard_changed(const macdrv_event *event) DECLSPEC_HIDDEN;

View File

@ -948,6 +948,222 @@ static void set_app_icon(void)
}
/**********************************************************************
* set_capture_window_for_move
*/
static BOOL set_capture_window_for_move(HWND hwnd)
{
HWND previous = 0;
BOOL ret;
SERVER_START_REQ(set_capture_window)
{
req->handle = wine_server_user_handle(hwnd);
req->flags = CAPTURE_MOVESIZE;
if ((ret = !wine_server_call_err(req)))
{
previous = wine_server_ptr_handle(reply->previous);
hwnd = wine_server_ptr_handle(reply->full_handle);
}
}
SERVER_END_REQ;
if (ret)
{
macdrv_SetCapture(hwnd, GUI_INMOVESIZE);
if (previous && previous != hwnd)
SendMessageW(previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd);
}
return ret;
}
/***********************************************************************
* move_window
*
* Based on user32's WINPOS_SysCommandSizeMove() specialized just for
* moving top-level windows and enforcing Mac-style constraints like
* keeping the top of the window within the work area.
*/
static LRESULT move_window(HWND hwnd, WPARAM wparam)
{
MSG msg;
RECT origRect, movedRect, desktopRect;
LONG hittest = (LONG)(wparam & 0x0f);
POINT capturePoint;
LONG style = GetWindowLongW(hwnd, GWL_STYLE);
BOOL moved = FALSE;
DWORD dwPoint = GetMessagePos();
INT captionHeight;
HMONITOR mon = 0;
MONITORINFO info;
if ((style & (WS_MINIMIZE | WS_MAXIMIZE)) || !IsWindowVisible(hwnd)) return -1;
if (hittest && hittest != HTCAPTION) return -1;
capturePoint.x = (short)LOWORD(dwPoint);
capturePoint.y = (short)HIWORD(dwPoint);
ClipCursor(NULL);
TRACE("hwnd %p hittest %d, pos %d,%d\n", hwnd, hittest, capturePoint.x, capturePoint.y);
origRect.left = origRect.right = origRect.top = origRect.bottom = 0;
if (AdjustWindowRectEx(&origRect, style, FALSE, GetWindowLongW(hwnd, GWL_EXSTYLE)))
captionHeight = -origRect.top;
else
captionHeight = 0;
GetWindowRect(hwnd, &origRect);
movedRect = origRect;
if (!hittest)
{
/* Move pointer to the center of the caption */
RECT rect = origRect;
/* Note: to be exactly centered we should take the different types
* of border into account, but it shouldn't make more than a few pixels
* of difference so let's not bother with that */
rect.top += GetSystemMetrics(SM_CYBORDER);
if (style & WS_SYSMENU)
rect.left += GetSystemMetrics(SM_CXSIZE) + 1;
if (style & WS_MINIMIZEBOX)
rect.right -= GetSystemMetrics(SM_CXSIZE) + 1;
if (style & WS_MAXIMIZEBOX)
rect.right -= GetSystemMetrics(SM_CXSIZE) + 1;
capturePoint.x = (rect.right + rect.left) / 2;
capturePoint.y = rect.top + GetSystemMetrics(SM_CYSIZE)/2;
SetCursorPos(capturePoint.x, capturePoint.y);
SendMessageW(hwnd, WM_SETCURSOR, (WPARAM)hwnd, MAKELONG(HTCAPTION, WM_MOUSEMOVE));
}
desktopRect = rect_from_cgrect(macdrv_get_desktop_rect());
mon = MonitorFromPoint(capturePoint, MONITOR_DEFAULTTONEAREST);
info.cbSize = sizeof(info);
if (mon && !GetMonitorInfoW(mon, &info))
mon = 0;
/* repaint the window before moving it around */
RedrawWindow(hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
SendMessageW(hwnd, WM_ENTERSIZEMOVE, 0, 0);
set_capture_window_for_move(hwnd);
while(1)
{
POINT pt;
int dx = 0, dy = 0;
HMONITOR newmon;
if (!GetMessageW(&msg, 0, 0, 0)) break;
if (CallMsgFilterW(&msg, MSGF_SIZE)) continue;
/* Exit on button-up, Return, or Esc */
if (msg.message == WM_LBUTTONUP ||
(msg.message == WM_KEYDOWN && (msg.wParam == VK_RETURN || msg.wParam == VK_ESCAPE)))
break;
if (msg.message != WM_KEYDOWN && msg.message != WM_MOUSEMOVE)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
continue; /* We are not interested in other messages */
}
pt = msg.pt;
if (msg.message == WM_KEYDOWN) switch(msg.wParam)
{
case VK_UP: pt.y -= 8; break;
case VK_DOWN: pt.y += 8; break;
case VK_LEFT: pt.x -= 8; break;
case VK_RIGHT: pt.x += 8; break;
}
pt.x = max(pt.x, desktopRect.left);
pt.x = min(pt.x, desktopRect.right - 1);
pt.y = max(pt.y, desktopRect.top);
pt.y = min(pt.y, desktopRect.bottom - 1);
if ((newmon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) && newmon != mon)
{
if (GetMonitorInfoW(newmon, &info))
mon = newmon;
else
mon = 0;
}
if (mon)
{
/* wineserver clips the cursor position to the virtual desktop rect but,
if the display configuration is non-rectangular, that could still
leave the logical cursor position outside of any display. The window
could keep moving as you push the cursor against a display edge, even
though the visible cursor doesn't keep moving. The following keeps
the window movement in sync with the visible cursor. */
pt.x = max(pt.x, info.rcMonitor.left);
pt.x = min(pt.x, info.rcMonitor.right - 1);
pt.y = max(pt.y, info.rcMonitor.top);
pt.y = min(pt.y, info.rcMonitor.bottom - 1);
/* Assuming that dx will be calculated below as pt.x - capturePoint.x,
dy will be pt.y - capturePoint.y, and movedRect will be offset by those,
we want to enforce these constraints:
movedRect.left + dx < info.rcWork.right
movedRect.right + dx > info.rcWork.left
movedRect.top + captionHeight + dy < info.rcWork.bottom
movedRect.bottom + dy > info.rcWork.top
movedRect.top + dy >= info.rcWork.top
The first four keep at least one edge barely in the work area.
The last keeps the top (i.e. the title bar) in the work area.
The fourth is redundant with the last, so can be ignored.
Substituting for dx and dy and rearranging gives us...
*/
pt.x = min(pt.x, info.rcWork.right - 1 + capturePoint.x - movedRect.left);
pt.x = max(pt.x, info.rcWork.left + 1 + capturePoint.x - movedRect.right);
pt.y = min(pt.y, info.rcWork.bottom - 1 + capturePoint.y - movedRect.top - captionHeight);
pt.y = max(pt.y, info.rcWork.top + capturePoint.y - movedRect.top);
}
dx = pt.x - capturePoint.x;
dy = pt.y - capturePoint.y;
if (dx || dy)
{
moved = TRUE;
if (msg.message == WM_KEYDOWN) SetCursorPos(pt.x, pt.y);
else
{
OffsetRect(&movedRect, dx, dy);
capturePoint = pt;
SendMessageW(hwnd, WM_MOVING, 0, (LPARAM)&movedRect);
SetWindowPos(hwnd, 0, movedRect.left, movedRect.top, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
}
}
set_capture_window_for_move(0);
SendMessageW(hwnd, WM_EXITSIZEMOVE, 0, 0);
SendMessageW(hwnd, WM_SETVISIBLE, TRUE, 0L);
/* if the move is canceled, restore the previous position */
if (moved && msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)
{
SetWindowPos(hwnd, 0, origRect.left, origRect.top, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
return 0;
}
/**********************************************************************
* CreateDesktopWindow (MACDRV.@)
*/
@ -1241,6 +1457,7 @@ LRESULT CDECL macdrv_SysCommand(HWND hwnd, WPARAM wparam, LPARAM lparam)
{
struct macdrv_win_data *data;
LRESULT ret = -1;
WPARAM command = wparam & 0xfff0;
TRACE("%p, %x, %lx\n", hwnd, (unsigned)wparam, lparam);
@ -1249,13 +1466,19 @@ LRESULT CDECL macdrv_SysCommand(HWND hwnd, WPARAM wparam, LPARAM lparam)
/* prevent a simple ALT press+release from activating the system menu,
as that can get confusing */
if ((wparam & 0xfff0) == SC_KEYMENU && !(WCHAR)lparam && !GetMenu(hwnd) &&
if (command == SC_KEYMENU && !(WCHAR)lparam && !GetMenu(hwnd) &&
(GetWindowLongW(hwnd, GWL_STYLE) & WS_SYSMENU))
{
TRACE("ignoring SC_KEYMENU wp %lx lp %lx\n", wparam, lparam);
ret = 0;
}
if (command == SC_MOVE)
{
release_win_data(data);
return move_window(hwnd, wparam);
}
done:
release_win_data(data);
return ret;