Ticket #284: menu-diagonal-movement2.patch

File menu-diagonal-movement2.patch, 6.7 KB (added by denis washington, 16 years ago)

Updated patch (fixes crasher; select item in navigation area after timeout; item clicked in navigation area shortly selects the item for feedback)

  • src/kits/interface/Menu.cpp

     
    13941394{
    13951395    // TODO: cleanup
    13961396    BMenuItem *item = NULL;
     1397    BRect navAreaRectAbove, navAreaRectBelow;
    13971398    bigtime_t openTime = system_time();
    1398    
     1399
    13991400    fState = MENU_STATE_TRACKING;
    14001401    if (fSuper != NULL)
    14011402        fSuper->fState = MENU_STATE_TRACKING_SUBMENU;
     
    14321433        bool overSub = _OverSubmenu(fSelected, screenLocation);
    14331434        item = _HitTestItems(location, B_ORIGIN);
    14341435        if (overSub) {
     1436            navAreaRectAbove = BRect();
     1437            navAreaRectBelow = BRect();
     1438
    14351439            // Since the submenu has its own looper,
    14361440            // we can unlock ours. Doing so also make sure
    14371441            // that our window gets any update message to
     
    14531457            if (!LockLooper())
    14541458                break;         
    14551459        } else if (item != NULL) {
    1456             _UpdateStateOpenSelect(item, openTime, mouseSpeed);
     1460            _UpdateStateOpenSelect(item, location, navAreaRectAbove,
     1461                navAreaRectBelow, openTime);
    14571462            if (!releasedOnce)
    14581463                releasedOnce = true;       
    14591464        } else if (_OverSuper(screenLocation)) {
     
    15281533
    15291534
    15301535void
    1531 BMenu::_UpdateStateOpenSelect(BMenuItem* item, bigtime_t& openTime,
    1532     const int32 &mouseSpeed)
     1536BMenu::_UpdateNavigationArea(BPoint position, BRect& navAreaRectAbove,
     1537    BRect& navAreaRectBelow)
    15331538{
     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
     1607void
     1608BMenu::_UpdateStateOpenSelect(BMenuItem* item, BPoint position,
     1609    BRect& navAreaRectAbove, BRect& navAreaRectBelow, bigtime_t& openTime)
     1610{
    15341611    if (fState == MENU_STATE_CLOSED)
    15351612        return;
    15361613
    15371614    if (item != fSelected) {
    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);
    15401623            openTime = system_time();
     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;
    15411651        } else {
    1542             //printf("Mouse moving too fast (%ld), ignoring...\n", mouseSpeed);     
     1652            float y_offset = navAreaRectBelow.bottom - position.y;
     1653            float ratio = navAreaRectBelow.Width() / navAreaRectBelow.Height();
     1654
     1655            inNavArea = y_offset >= (navAreaRectBelow.Height() - x_offset / ratio);
    15431656        }
    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        }
    15501663    }
    15511664    if (fState != MENU_STATE_TRACKING)
    15521665        fState = MENU_STATE_TRACKING;
     
    15611674        return;
    15621675
    15631676    if (buttons != 0 && _IsStickyMode()) {
    1564         if (item == NULL)
     1677        if (item == NULL) {
     1678            if (item != fSelected) {
     1679                LockLooper();
     1680                _SelectItem(item, false);
     1681                UnlockLooper();
     1682            }
    15651683            fState = MENU_STATE_CLOSED;
    1566         else
     1684        } else
    15671685            _SetStickyMode(false);
    15681686    } else if (buttons == 0 && !_IsStickyMode()) {
    15691687        if (fExtraRect != NULL && fExtraRect->Contains(where)) {
     
    15711689            fExtraRect = NULL;
    15721690                // Setting this to NULL will prevent this code
    15731691                // to be executed next time
    1574         } else
     1692        } else {
     1693            if (item != fSelected) {
     1694                LockLooper();
     1695                _SelectItem(item, false);
     1696                UnlockLooper();
     1697            }
    15751698            fState = MENU_STATE_CLOSED;
     1699        }
    15761700    }
    15771701}
    15781702
  • src/kits/interface/MenuBar.cpp

     
    562562
    563563        if (fChosenItem != NULL)
    564564            fChosenItem->Invoke();
     565
    565566        _RestoreFocus();
    566567        window->Unlock();
    567568    }