Ticket #284: menu-diagonal-movement.patch

File menu-diagonal-movement.patch, 5.7 KB (added by denis washington, 17 years ago)

Patch for #284

  • src/kits/interface/Menu.cpp

     
    13941394{
    13951395    // TODO: cleanup
    13961396    BMenuItem *item = NULL;
    1397     bigtime_t openTime = system_time();
    1398    
     1397    BRect navAreaRectAbove, navAreaRectBelow;
     1398
    13991399    fState = MENU_STATE_TRACKING;
    14001400    if (fSuper != NULL)
    14011401        fSuper->fState = MENU_STATE_TRACKING_SUBMENU;
     
    14321432        bool overSub = _OverSubmenu(fSelected, screenLocation);
    14331433        item = _HitTestItems(location, B_ORIGIN);
    14341434        if (overSub) {
     1435            navAreaRectAbove = BRect();
     1436            navAreaRectBelow = BRect();
     1437
    14351438            // Since the submenu has its own looper,
    14361439            // we can unlock ours. Doing so also make sure
    14371440            // that our window gets any update message to
     
    14531456            if (!LockLooper())
    14541457                break;         
    14551458        } else if (item != NULL) {
    1456             _UpdateStateOpenSelect(item, openTime, mouseSpeed);
     1459            _UpdateStateOpenSelect(item, location, navAreaRectAbove, navAreaRectBelow);
    14571460            if (!releasedOnce)
    14581461                releasedOnce = true;       
    14591462        } else if (_OverSuper(screenLocation)) {
     
    15281531
    15291532
    15301533void
    1531 BMenu::_UpdateStateOpenSelect(BMenuItem* item, bigtime_t& openTime,
    1532     const int32 &mouseSpeed)
     1534BMenu::_UpdateNavigationArea(BPoint position, BRect& navAreaRectAbove,
     1535    BRect& navAreaRectBelow)
    15331536{
     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
     1606void
     1607BMenu::_UpdateStateOpenSelect(BMenuItem* item, BPoint position,
     1608    BRect& navAreaRectAbove, BRect& navAreaRectBelow)
     1609{
    15341610    if (fState == MENU_STATE_CLOSED)
    15351611        return;
    15361612
    15371613    if (item != fSelected) {
    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;
    15411650        } else {
    1542             //printf("Mouse moving too fast (%ld), ignoring...\n", mouseSpeed);     
     1651            float y_offset = navAreaRectBelow.bottom - position.y;
     1652            float ratio = navAreaRectBelow.Width() / navAreaRectBelow.Height();
     1653
     1654            inNavArea = y_offset >= (navAreaRectBelow.Height() - x_offset / ratio);
    15431655        }
    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);
     1656
     1657        if (!inNavArea) {
     1658            _SelectItem(item);
     1659            _UpdateNavigationArea(position, navAreaRectAbove, navAreaRectBelow);
     1660        }
    15501661    }
    15511662    if (fState != MENU_STATE_TRACKING)
    15521663        fState = MENU_STATE_TRACKING;