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