| 1539 | #define NAV_AREA_THRESHOLD 3 |
| 1540 | |
| 1541 | // The navigation area is a region in which mouse-overs won't select |
| 1542 | // the item under the cursor. This makes it easier to navigate to |
| 1543 | // submenus, as the cursor can be moved to submenu items directly instead |
| 1544 | // of having to move it horizontally into the submenu first. The concept |
| 1545 | // is illustrated below: |
| 1546 | // |
| 1547 | // +-------+----+---------+ |
| 1548 | // | | /| | |
| 1549 | // | | /*| | |
| 1550 | // |[2]--> | /**| | |
| 1551 | // | |/[4]| | |
| 1552 | // |------------| | |
| 1553 | // | [1] | [6] | |
| 1554 | // |------------| | |
| 1555 | // | |\[5]| | |
| 1556 | // |[3]--> | \**| | |
| 1557 | // | | \*| | |
| 1558 | // | | \| | |
| 1559 | // | +----|---------+ |
| 1560 | // | | |
| 1561 | // +------------+ |
| 1562 | // |
| 1563 | // [1] Selected item, cursor position ('position') |
| 1564 | // [2] Upper navigation area rectangle ('navAreaRectAbove') |
| 1565 | // [3] Lower navigation area rectangle ('navAreaRectBelow') |
| 1566 | // [4] Upper navigation area |
| 1567 | // [5] Lower navigation area |
| 1568 | // [6] Submenu |
| 1569 | // |
| 1570 | // The rectangles are used to calculate if the cursor is in the actual |
| 1571 | // navigation area (see _UpdateStateOpenSelect()). |
| 1572 | |
| 1573 | if (fSelected == NULL) |
| 1574 | return; |
| 1575 | |
| 1576 | BMenu *submenu = fSelected->Submenu(); |
| 1577 | |
| 1578 | if (submenu != NULL) { |
| 1579 | BRect menuBounds = ConvertToScreen(Bounds()); |
| 1580 | |
| 1581 | fSelected->Submenu()->LockLooper(); |
| 1582 | BRect submenuBounds = fSelected->Submenu()->ConvertToScreen( |
| 1583 | fSelected->Submenu()->Bounds()); |
| 1584 | fSelected->Submenu()->UnlockLooper(); |
| 1585 | |
| 1586 | if (menuBounds.left < submenuBounds.left) { |
| 1587 | navAreaRectAbove.Set(position.x + NAV_AREA_THRESHOLD, |
| 1588 | submenuBounds.top, menuBounds.right, |
| 1589 | position.y); |
| 1590 | navAreaRectBelow.Set(position.x + NAV_AREA_THRESHOLD, |
| 1591 | position.y, menuBounds.right, |
| 1592 | submenuBounds.bottom); |
| 1593 | } else { |
| 1594 | navAreaRectAbove.Set(menuBounds.left, |
| 1595 | submenuBounds.top, position.x - NAV_AREA_THRESHOLD, |
| 1596 | position.y); |
| 1597 | navAreaRectBelow.Set(menuBounds.left, |
| 1598 | position.y, position.x - NAV_AREA_THRESHOLD, |
| 1599 | submenuBounds.bottom); |
| 1600 | } |
| 1601 | } else { |
| 1602 | navAreaRectAbove = BRect(); |
| 1603 | navAreaRectBelow = BRect(); |
| 1604 | } |
| 1605 | } |
| 1606 | |
| 1607 | void |
| 1608 | BMenu::_UpdateStateOpenSelect(BMenuItem* item, BPoint position, |
| 1609 | BRect& navAreaRectAbove, BRect& navAreaRectBelow, bigtime_t& openTime) |
| 1610 | { |
1538 | | if (mouseSpeed < kMouseMotionThreshold) { |
1539 | | _SelectItem(item, false); |
| 1615 | position = ConvertToScreen(position); |
| 1616 | |
| 1617 | bool inNavAreaRectAbove = navAreaRectAbove.Contains(position); |
| 1618 | bool inNavAreaRectBelow = navAreaRectBelow.Contains(position); |
| 1619 | |
| 1620 | if (!inNavAreaRectAbove && !inNavAreaRectBelow) { |
| 1621 | _SelectItem(item); |
| 1622 | _UpdateNavigationArea(position, navAreaRectAbove, navAreaRectBelow); |
| 1624 | return; |
| 1625 | } |
| 1626 | |
| 1627 | BRect menuBounds = ConvertToScreen(Bounds()); |
| 1628 | |
| 1629 | fSelected->Submenu()->LockLooper(); |
| 1630 | BRect submenuBounds = fSelected->Submenu()->ConvertToScreen( |
| 1631 | fSelected->Submenu()->Bounds()); |
| 1632 | fSelected->Submenu()->UnlockLooper(); |
| 1633 | |
| 1634 | float x_offset; |
| 1635 | |
| 1636 | // navAreaRectAbove and navAreaRectBelow have the same X |
| 1637 | // position and width, so it doesn't matter which one we use to |
| 1638 | // calculate the X offset |
| 1639 | if (menuBounds.left < submenuBounds.left) |
| 1640 | x_offset = position.x - navAreaRectAbove.left; |
| 1641 | else |
| 1642 | x_offset = navAreaRectAbove.right - position.x; |
| 1643 | |
| 1644 | bool inNavArea; |
| 1645 | |
| 1646 | if (inNavAreaRectAbove) { |
| 1647 | float y_offset = navAreaRectAbove.bottom - position.y; |
| 1648 | float ratio = navAreaRectAbove.Width() / navAreaRectAbove.Height(); |
| 1649 | |
| 1650 | inNavArea = y_offset <= x_offset / ratio; |
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); |
| 1657 | |
| 1658 | if (!inNavArea || system_time() > openTime + 1500000) { |
| 1659 | _SelectItem(item); |
| 1660 | _UpdateNavigationArea(position, navAreaRectAbove, navAreaRectBelow); |
| 1661 | openTime = system_time(); |
| 1662 | } |