Ticket #284: menu-diagonal-movement3.patch

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

Second patch update (includes header changes; further fixes to timeout; open submenus after a 0.2 sec delay to make menu browsing smoother - moving a cursor quickly down a menu doesn't cause submenus to quickly appear and disappear while the cursor flies over an item with submenu)

  • src/kits/interface/Menu.cpp

     
    13671367        _SelectItem(NULL);
    13681368
    13691369    window->Hide();
    1370     window->DetachMenu();
     1370    window->DetachMenu();           printf("time set\n");
     1371
    13711372        // we don't want to be deleted when the window is removed
    13721373
    13731374#if USE_CACHED_MENUWINDOW
     
    13841385}
    13851386
    13861387
     1388const static bigtime_t kOpenSubmenuDelay = 225000;
     1389const static bigtime_t kNavigationAreaTimeout = 1000000;
    13871390const static bigtime_t kHysteresis = 200000; // TODO: Test and reduce if needed.
    13881391const static int32 kMouseMotionThreshold = 15;
    13891392    // TODO: Same as above. Actually, we could get rid of the kHysteresis
     
    13941397{
    13951398    // TODO: cleanup
    13961399    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
    13991404    fState = MENU_STATE_TRACKING;
    14001405    if (fSuper != NULL)
    14011406        fSuper->fState = MENU_STATE_TRACKING_SUBMENU;
     
    14321437        bool overSub = _OverSubmenu(fSelected, screenLocation);
    14331438        item = _HitTestItems(location, B_ORIGIN);
    14341439        if (overSub) {
     1440            navAreaRectAbove = BRect();
     1441            navAreaRectBelow = BRect();
     1442
    14351443            // Since the submenu has its own looper,
    14361444            // we can unlock ours. Doing so also make sure
    14371445            // that our window gets any update message to
     
    14531461            if (!LockLooper())
    14541462                break;         
    14551463        } else if (item != NULL) {
    1456             _UpdateStateOpenSelect(item, openTime, mouseSpeed);
     1464            _UpdateStateOpenSelect(item, location, navAreaRectAbove,
     1465                navAreaRectBelow, selectedTime, navigationAreaTime);
    14571466            if (!releasedOnce)
    14581467                releasedOnce = true;       
    14591468        } else if (_OverSuper(screenLocation)) {
     
    15281537
    15291538
    15301539void
    1531 BMenu::_UpdateStateOpenSelect(BMenuItem* item, bigtime_t& openTime,
    1532     const int32 &mouseSpeed)
     1540BMenu::_UpdateNavigationArea(BPoint position, BRect& navAreaRectAbove,
     1541    BRect& navAreaRectBelow)
    15331542{
     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
     1615void
     1616BMenu::_UpdateStateOpenSelect(BMenuItem* item, BPoint position,
     1617    BRect& navAreaRectAbove, BRect& navAreaRectBelow, bigtime_t& selectedTime,
     1618    bigtime_t& navigationAreaTime)
     1619{
    15341620    if (fState == MENU_STATE_CLOSED)
    15351621        return;
    15361622
     1623
    15371624    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) {
    15391634            _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;
    15411666        } 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);
    15431671        }
    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        }
    15501700    }
     1701
    15511702    if (fState != MENU_STATE_TRACKING)
    15521703        fState = MENU_STATE_TRACKING;
    15531704}
     
    15611712        return;
    15621713
    15631714    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            }
    15651721            fState = MENU_STATE_CLOSED;
    1566         else
     1722        } else
    15671723            _SetStickyMode(false);
    15681724    } else if (buttons == 0 && !_IsStickyMode()) {
    15691725        if (fExtraRect != NULL && fExtraRect->Contains(where)) {
     
    15711727            fExtraRect = NULL;
    15721728                // Setting this to NULL will prevent this code
    15731729                // to be executed next time
    1574         } else
     1730        } else {
     1731            if (item != fSelected) {
     1732                LockLooper();
     1733                _SelectItem(item, false);
     1734                UnlockLooper();
     1735            }
    15751736            fState = MENU_STATE_CLOSED;
     1737        }
    15761738    }
    15771739}
    15781740
  • headers/os/interface/Menu.h

     
    180180            void            _Hide();
    181181            BMenuItem*      _Track(int* action, long start = -1);
    182182
     183            void            _UpdateNavigationArea(BPoint position,
     184                                BRect& navAreaRectAbove, BRect& navAreaBelow);
     185
    183186            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);
    185190            void            _UpdateStateClose(BMenuItem* item, const BPoint& where,
    186191                                const uint32& buttons);
    187192