Ticket #284: menu-diagonal-movement3.patch
File menu-diagonal-movement3.patch, 8.7 KB (added by , 17 years ago) |
---|
-
src/kits/interface/Menu.cpp
1367 1367 _SelectItem(NULL); 1368 1368 1369 1369 window->Hide(); 1370 window->DetachMenu(); 1370 window->DetachMenu(); printf("time set\n"); 1371 1371 1372 // we don't want to be deleted when the window is removed 1372 1373 1373 1374 #if USE_CACHED_MENUWINDOW … … 1384 1385 } 1385 1386 1386 1387 1388 const static bigtime_t kOpenSubmenuDelay = 225000; 1389 const static bigtime_t kNavigationAreaTimeout = 1000000; 1387 1390 const static bigtime_t kHysteresis = 200000; // TODO: Test and reduce if needed. 1388 1391 const static int32 kMouseMotionThreshold = 15; 1389 1392 // TODO: Same as above. Actually, we could get rid of the kHysteresis … … 1394 1397 { 1395 1398 // TODO: cleanup 1396 1399 BMenuItem *item = NULL; 1397 bigtime_t openTime = system_time(); 1398 1400 BRect navAreaRectAbove, navAreaRectBelow; 1401 bigtime_t selectedTime = system_time(); 1402 bigtime_t navigationAreaTime = 0; 1403 1399 1404 fState = MENU_STATE_TRACKING; 1400 1405 if (fSuper != NULL) 1401 1406 fSuper->fState = MENU_STATE_TRACKING_SUBMENU; … … 1432 1437 bool overSub = _OverSubmenu(fSelected, screenLocation); 1433 1438 item = _HitTestItems(location, B_ORIGIN); 1434 1439 if (overSub) { 1440 navAreaRectAbove = BRect(); 1441 navAreaRectBelow = BRect(); 1442 1435 1443 // Since the submenu has its own looper, 1436 1444 // we can unlock ours. Doing so also make sure 1437 1445 // that our window gets any update message to … … 1453 1461 if (!LockLooper()) 1454 1462 break; 1455 1463 } else if (item != NULL) { 1456 _UpdateStateOpenSelect(item, openTime, mouseSpeed); 1464 _UpdateStateOpenSelect(item, location, navAreaRectAbove, 1465 navAreaRectBelow, selectedTime, navigationAreaTime); 1457 1466 if (!releasedOnce) 1458 1467 releasedOnce = true; 1459 1468 } else if (_OverSuper(screenLocation)) { … … 1528 1537 1529 1538 1530 1539 void 1531 BMenu::_Update StateOpenSelect(BMenuItem* item, bigtime_t& openTime,1532 const int32 &mouseSpeed)1540 BMenu::_UpdateNavigationArea(BPoint position, BRect& navAreaRectAbove, 1541 BRect& navAreaRectBelow) 1533 1542 { 1543 #define NAV_AREA_THRESHOLD 8 1544 1545 // The navigation area is a region in which mouse-overs won't select 1546 // the item under the cursor. This makes it easier to navigate to 1547 // submenus, as the cursor can be moved to submenu items directly instead 1548 // of having to move it horizontally into the submenu first. The concept 1549 // is illustrated below: 1550 // 1551 // +-------+----+---------+ 1552 // | | /| | 1553 // | | /*| | 1554 // |[2]--> | /**| | 1555 // | |/[4]| | 1556 // |------------| | 1557 // | [1] | [6] | 1558 // |------------| | 1559 // | |\[5]| | 1560 // |[3]--> | \**| | 1561 // | | \*| | 1562 // | | \| | 1563 // | +----|---------+ 1564 // | | 1565 // +------------+ 1566 // 1567 // [1] Selected item, cursor position ('position') 1568 // [2] Upper navigation area rectangle ('navAreaRectAbove') 1569 // [3] Lower navigation area rectangle ('navAreaRectBelow') 1570 // [4] Upper navigation area 1571 // [5] Lower navigation area 1572 // [6] Submenu 1573 // 1574 // The rectangles are used to calculate if the cursor is in the actual 1575 // navigation area (see _UpdateStateOpenSelect()). 1576 1577 if (fSelected == NULL) 1578 return; 1579 1580 BMenu *submenu = fSelected->Submenu(); 1581 1582 if (submenu != NULL) { 1583 BRect menuBounds = ConvertToScreen(Bounds()); 1584 1585 fSelected->Submenu()->LockLooper(); 1586 BRect submenuBounds = fSelected->Submenu()->ConvertToScreen( 1587 fSelected->Submenu()->Bounds()); 1588 fSelected->Submenu()->UnlockLooper(); 1589 1590 if (menuBounds.left < submenuBounds.left) { 1591 navAreaRectAbove.Set(position.x + NAV_AREA_THRESHOLD, 1592 submenuBounds.top, menuBounds.right, 1593 position.y); 1594 navAreaRectBelow.Set(position.x + NAV_AREA_THRESHOLD, 1595 position.y, menuBounds.right, 1596 submenuBounds.bottom); 1597 } else { 1598 navAreaRectAbove.Set(menuBounds.left, 1599 submenuBounds.top, position.x - NAV_AREA_THRESHOLD, 1600 position.y); 1601 navAreaRectBelow.Set(menuBounds.left, 1602 position.y, position.x - NAV_AREA_THRESHOLD, 1603 submenuBounds.bottom); 1604 } 1605 position.PrintToStream(); 1606 navAreaRectAbove.PrintToStream(); 1607 navAreaRectBelow.PrintToStream(); 1608 printf("\n"); 1609 } else { 1610 navAreaRectAbove = BRect(); 1611 navAreaRectBelow = BRect(); 1612 } 1613 } 1614 1615 void 1616 BMenu::_UpdateStateOpenSelect(BMenuItem* item, BPoint position, 1617 BRect& navAreaRectAbove, BRect& navAreaRectBelow, bigtime_t& selectedTime, 1618 bigtime_t& navigationAreaTime) 1619 { 1534 1620 if (fState == MENU_STATE_CLOSED) 1535 1621 return; 1536 1622 1623 1537 1624 if (item != fSelected) { 1538 if (mouseSpeed < kMouseMotionThreshold) { 1625 if (navigationAreaTime == 0) 1626 navigationAreaTime = system_time(); 1627 1628 position = ConvertToScreen(position); 1629 1630 bool inNavAreaRectAbove = navAreaRectAbove.Contains(position); 1631 bool inNavAreaRectBelow = navAreaRectBelow.Contains(position); 1632 1633 if (!inNavAreaRectAbove && !inNavAreaRectBelow) { 1539 1634 _SelectItem(item, false); 1540 openTime = system_time(); 1635 navAreaRectAbove = BRect(); 1636 navAreaRectBelow = BRect(); 1637 selectedTime = system_time(); 1638 navigationAreaTime = 0; 1639 return; 1640 } 1641 1642 BRect menuBounds = ConvertToScreen(Bounds()); 1643 1644 fSelected->Submenu()->LockLooper(); 1645 BRect submenuBounds = fSelected->Submenu()->ConvertToScreen( 1646 fSelected->Submenu()->Bounds()); 1647 fSelected->Submenu()->UnlockLooper(); 1648 1649 float x_offset; 1650 1651 // navAreaRectAbove and navAreaRectBelow have the same X 1652 // position and width, so it doesn't matter which one we use to 1653 // calculate the X offset 1654 if (menuBounds.left < submenuBounds.left) 1655 x_offset = position.x - navAreaRectAbove.left; 1656 else 1657 x_offset = navAreaRectAbove.right - position.x; 1658 1659 bool inNavArea; 1660 1661 if (inNavAreaRectAbove) { 1662 float y_offset = navAreaRectAbove.bottom - position.y; 1663 float ratio = navAreaRectAbove.Width() / navAreaRectAbove.Height(); 1664 1665 inNavArea = y_offset <= x_offset / ratio; 1541 1666 } else { 1542 //printf("Mouse moving too fast (%ld), ignoring...\n", mouseSpeed); 1667 float y_offset = navAreaRectBelow.bottom - position.y; 1668 float ratio = navAreaRectBelow.Width() / navAreaRectBelow.Height(); 1669 1670 inNavArea = y_offset >= (navAreaRectBelow.Height() - x_offset / ratio); 1543 1671 } 1544 } else if (system_time() > kHysteresis + openTime && item->Submenu() != NULL 1545 && item->Submenu()->Window() == NULL) { 1546 // Open the submenu if it's not opened yet, but only if 1547 // the mouse pointer stayed over there for some time 1548 // (hysteresis) 1549 _SelectItem(item); 1672 1673 bigtime_t systime = system_time(); 1674 1675 if (!inNavArea || (navigationAreaTime > 0 && systime - 1676 navigationAreaTime > kNavigationAreaTimeout)) { 1677 // Don't delay opening of submenu if the user had 1678 // to wait for the navigation area timeout anyway 1679 _SelectItem(item, inNavArea); 1680 1681 if (inNavArea) { 1682 _UpdateNavigationArea(position, navAreaRectAbove, 1683 navAreaRectBelow); 1684 } else { 1685 navAreaRectAbove = BRect(); 1686 navAreaRectBelow = BRect(); 1687 } 1688 1689 selectedTime = system_time(); 1690 navigationAreaTime = 0; 1691 } 1692 } else if (fSelected->Submenu() != NULL && 1693 system_time() - selectedTime > kOpenSubmenuDelay) { 1694 _SelectItem(fSelected, true); 1695 1696 if (!navAreaRectAbove.IsValid() && !navAreaRectBelow.IsValid()) { 1697 position = ConvertToScreen(position); 1698 _UpdateNavigationArea(position, navAreaRectAbove, navAreaRectBelow); 1699 } 1550 1700 } 1701 1551 1702 if (fState != MENU_STATE_TRACKING) 1552 1703 fState = MENU_STATE_TRACKING; 1553 1704 } … … 1561 1712 return; 1562 1713 1563 1714 if (buttons != 0 && _IsStickyMode()) { 1564 if (item == NULL) 1715 if (item == NULL) { 1716 if (item != fSelected) { 1717 LockLooper(); 1718 _SelectItem(item, false); 1719 UnlockLooper(); 1720 } 1565 1721 fState = MENU_STATE_CLOSED; 1566 else1722 } else 1567 1723 _SetStickyMode(false); 1568 1724 } else if (buttons == 0 && !_IsStickyMode()) { 1569 1725 if (fExtraRect != NULL && fExtraRect->Contains(where)) { … … 1571 1727 fExtraRect = NULL; 1572 1728 // Setting this to NULL will prevent this code 1573 1729 // to be executed next time 1574 } else 1730 } else { 1731 if (item != fSelected) { 1732 LockLooper(); 1733 _SelectItem(item, false); 1734 UnlockLooper(); 1735 } 1575 1736 fState = MENU_STATE_CLOSED; 1737 } 1576 1738 } 1577 1739 } 1578 1740 -
headers/os/interface/Menu.h
180 180 void _Hide(); 181 181 BMenuItem* _Track(int* action, long start = -1); 182 182 183 void _UpdateNavigationArea(BPoint position, 184 BRect& navAreaRectAbove, BRect& navAreaBelow); 185 183 186 void _UpdateStateOpenSelect(BMenuItem* item, 184 bigtime_t& openTime, const int32 &mouseSpeed); 187 BPoint position, BRect& navAreaRectAbove, 188 BRect& navAreaBelow, bigtime_t& selectedTime, 189 bigtime_t& navigationAreaTime); 185 190 void _UpdateStateClose(BMenuItem* item, const BPoint& where, 186 191 const uint32& buttons); 187 192