bool WebPopupMenuProxyWin::scrollToRevealSelection() { if (!m_scrollbar) return false; int index = focusedIndex(); if (index < m_scrollOffset) { ScrollableArea::scrollToYOffsetWithoutAnimation(index); return true; } if (index >= m_scrollOffset + visibleItems()) { ScrollableArea::scrollToYOffsetWithoutAnimation(index - visibleItems() + 1); return true; } return false; }
void VarTree::setSliderFromItem( const Iterator& it ) { VarPercent &rVarPos = getPositionVar(); int indexMax = m_flat ? (countLeafs() - 1) : (visibleItems() - 1); int index = getIndex( it ); double percentage = (1.0 - (double)index/(double)indexMax); m_dontMove = true; rVarPos.set( (float)percentage ); m_dontMove = false; }
VarTree::Iterator VarTree::getItemFromSlider() { // a simple (int)(...) causes rounding errors ! #ifdef _MSC_VER # define lrint (int) #endif VarPercent &rVarPos = getPositionVar(); double percentage = rVarPos.get(); int indexMax = m_flat ? (countLeafs() - 1) : (visibleItems() - 1); int index = lrint( (1.0 - percentage)*(double)indexMax ); Iterator it_first = m_flat ? getLeaf( index + 1 ) : getVisibleItem( index + 1 ); return it_first; }
LRESULT WebPopupMenuProxyWin::onKeyDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) { handled = true; LRESULT lResult = 0; switch (LOWORD(wParam)) { case VK_DOWN: case VK_RIGHT: down(); break; case VK_UP: case VK_LEFT: up(); break; case VK_HOME: focusFirst(); break; case VK_END: focusLast(); break; case VK_PRIOR: if (focusedIndex() != scrollOffset()) { // Set the selection to the first visible item int firstVisibleItem = scrollOffset(); up(focusedIndex() - firstVisibleItem); } else { // The first visible item is selected, so move the selection back one page up(visibleItems()); } break; case VK_NEXT: { int lastVisibleItem = scrollOffset() + visibleItems() - 1; if (focusedIndex() != lastVisibleItem) { // Set the selection to the last visible item down(lastVisibleItem - focusedIndex()); } else { // The last visible item is selected, so move the selection forward one page down(visibleItems()); } break; } case VK_TAB: ::SendMessage(m_webView->window(), message, wParam, lParam); hide(); break; case VK_ESCAPE: hide(); break; default: if (isASCIIPrintable(wParam)) { // Send the keydown to the WebView so it can be used for type-to-select. // Since we know that the virtual key is ASCII printable, it's OK to convert this to // a WM_CHAR message. (We don't want to call TranslateMessage because that will post a // WM_CHAR message that will be stolen and redirected to the popup HWND. ::PostMessage(m_popup, WM_HOST_WINDOW_CHAR, wParam, lParam); } else lResult = 1; break; } return lResult; }
void WebPopupMenuProxyWin::showPopupMenu(const IntRect& rect, TextDirection, double, const Vector<WebPopupItem>& items, const PlatformPopupMenuData& data, int32_t selectedIndex) { m_items = items; m_data = data; m_newSelectedIndex = selectedIndex; calculatePositionAndSize(rect); if (clientRect().isEmpty()) return; HWND hostWindow = m_webView->window(); if (!m_scrollbar && visibleItems() < m_items.size()) { m_scrollbar = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, SmallScrollbar); m_scrollbar->styleChanged(); } if (!m_popup) { registerWindowClass(); DWORD exStyle = WS_EX_LTRREADING; m_popup = ::CreateWindowEx(exStyle, kWebKit2WebPopupMenuProxyWindowClassName, TEXT("PopupMenu"), WS_POPUP | WS_BORDER, m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), hostWindow, 0, instanceHandle(), this); if (!m_popup) return; } BOOL shouldAnimate = FALSE; ::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0); if (shouldAnimate) { RECT viewRect = {0}; ::GetWindowRect(hostWindow, &viewRect); if (!::IsRectEmpty(&viewRect)) { // Popups should slide into view away from the <select> box // NOTE: This may have to change for Vista DWORD slideDirection = (m_windowRect.y() < viewRect.top + rect.location().y()) ? AW_VER_NEGATIVE : AW_VER_POSITIVE; ::AnimateWindow(m_popup, defaultAnimationDuration, AW_SLIDE | slideDirection); } } else ::ShowWindow(m_popup, SW_SHOWNOACTIVATE); int index = selectedIndex; if (index >= 0) setFocusedIndex(index); m_showPopup = true; // Protect the popup menu in case its owner is destroyed while we're running the message pump. RefPtr<WebPopupMenuProxyWin> protect(this); ::SetCapture(hostWindow); MSG msg; HWND activeWindow; while (::GetMessage(&msg, 0, 0, 0)) { switch (msg.message) { case WM_HOST_WINDOW_MOUSEMOVE: case WM_HOST_WINDOW_CHAR: if (msg.hwnd == m_popup) { // This message should be sent to the host window. msg.hwnd = hostWindow; msg.message -= WM_HOST_WINDOW_FIRST; } break; // Steal mouse messages. case WM_NCMOUSEMOVE: case WM_NCLBUTTONDOWN: case WM_NCLBUTTONUP: case WM_NCLBUTTONDBLCLK: case WM_NCRBUTTONDOWN: case WM_NCRBUTTONUP: case WM_NCRBUTTONDBLCLK: case WM_NCMBUTTONDOWN: case WM_NCMBUTTONUP: case WM_NCMBUTTONDBLCLK: case WM_MOUSEWHEEL: msg.hwnd = m_popup; break; // These mouse messages use client coordinates so we need to convert them. case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: { // Translate the coordinate. translatePoint(msg.lParam, msg.hwnd, m_popup); msg.hwnd = m_popup; break; } // Steal all keyboard messages. case WM_KEYDOWN: case WM_KEYUP: case WM_CHAR: case WM_DEADCHAR: case WM_SYSKEYUP: case WM_SYSCHAR: case WM_SYSDEADCHAR: msg.hwnd = m_popup; break; } ::TranslateMessage(&msg); ::DispatchMessage(&msg); if (!m_showPopup) break; activeWindow = ::GetActiveWindow(); if (activeWindow != hostWindow && !::IsChild(activeWindow, hostWindow)) break; if (::GetCapture() != hostWindow) break; } if (::GetCapture() == hostWindow) ::ReleaseCapture(); m_showPopup = false; ::ShowWindow(m_popup, SW_HIDE); if (!m_client) return; m_client->valueChangedForPopupMenu(this, m_newSelectedIndex); // <https://bugs.webkit.org/show_bug.cgi?id=57904> In order to properly call the onClick() // handler on a <select> element, we need to fake a mouse up event in the main window. // The main window already received the mouse down, which showed this popup, but upon // selection of an item the mouse up gets eaten by the popup menu. So we take the mouse down // event, change the message type to a mouse up event, and post that in the message queue. // Thus, we are virtually clicking at the // same location where the mouse down event occurred. This allows the hit test to select // the correct element, and thereby call the onClick() JS handler. if (!m_client->currentlyProcessedMouseDownEvent()) return; const MSG* initiatingWinEvent = m_client->currentlyProcessedMouseDownEvent()->nativeEvent(); MSG fakeEvent = *initiatingWinEvent; fakeEvent.message = WM_LBUTTONUP; ::PostMessage(fakeEvent.hwnd, fakeEvent.message, fakeEvent.wParam, fakeEvent.lParam); }