Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Feb 20, 2011, 3:24:55 AM (13 years ago)
Author:
landauf
Message:

greatly improved keyboard controlled navigation in menus with sparse button tables (deactivated buttons, deactivated rows or columns, holes, randomly placed buttons, …)

Location:
code/branches/usability/data/gui/scripts
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • code/branches/usability/data/gui/scripts/GUISheet.lua

    r7925 r7926  
    8282    if self.buttons then
    8383        if code == "208" then     -- key down
    84             self:moveSelection(1, 0)
     84            self:moveSelectionRow(1)
    8585        elseif code == "200" then -- key up
    86             self:moveSelection(-1, 0)
     86            self:moveSelectionRow(-1)
    8787        elseif code == "205" then -- key right
    88             self:moveSelection(0, 1)
     88            self:moveSelectionColumn(1)
    8989        elseif code == "203" then -- key left
    90             self:moveSelection(0, -1)
     90            self:moveSelectionColumn(-1)
    9191        elseif code == "28"  then -- key enter
    9292            self:pressSelectedButton()
     
    107107
    108108-- Initializes the buttons table, used to control the menu with the keyboard
    109 function P:initButtons(rows, columns)
     109-- ratio: the button's with divided by the button's height (used to calculate distance between buttons - adjust this until you get the desired behavior)
     110function P:initButtons(rows, columns, ratio)
    110111    self.rows = rows
    111112    self.columns = columns
     
    113114    self.selectedRow = 0
    114115    self.selectedColumn = 0
     116
     117    if ratio then
     118        self.ratio = ratio
     119    else
     120        self.ratio = 1
     121    end
    115122end
    116123
     
    125132-- Returns the button at a given position in the table. The upper-left button is at position (1, 1)
    126133function P:getButton(row, column)
    127     assert(row > 0 and column > 0 and row <= self.rows and column <= self.columns, "(" .. row .. "/" .. column .. ") is not in the valid bounds of the table (1/1)-(" .. self.rows .. "/" .. self.columns .. ")")
    128 
    129134    return self.buttons[(row - 1) * self.columns + (column - 1)]
    130135end
     
    132137-- Returns the selected button
    133138function P:getSelectedButton()
    134     assert(self.selectedRow > 0 and self.selectedColumn > 0, "no button selected")
    135 
    136139    return self:getButton(self.selectedRow, self.selectedColumn)
    137140end
     
    160163end
    161164
    162 -- Moves the selection by a given number of rows and columns (positive values mean increasing row/column, negative values mean decreasing row/column)
    163 function P:moveSelection(relRow, relColumn)
     165-- Moves the selection by a given number of rows (a positive value means down, a negative value means up)
     166function P:moveSelectionRow(relRow)
     167    self:moveSelection(relRow, "selectedRow", "selectedColumn", "rows", "columns", true)
     168end
     169
     170-- Moves the selection by a given number of columns (a positive value means right, a negative value means left)
     171function P:moveSelectionColumn(relColumn)
     172    self:moveSelection(relColumn, "selectedColumn", "selectedRow", "columns", "rows", false)
     173end
     174
     175-- Generic move function, the values are determined at runtime depending on the arguments
     176function P:moveSelection(relMove, selectedThis, selectedOther, limitThis, limitOther, isRow)
    164177    -- if there's no selection yet, prepare it such that the selection enters the table from the desired side
    165     if self:hasSelection() == false then
    166         -- note: we assume either relRow or relColumn is 0, thus no diagonal movement. therefore the following checks can be separated
    167         if relRow > 0 then
    168             self.selectedRow = 0
    169             self.selectedColumn = 1
    170         elseif relRow < 0 then
    171             self.selectedRow = self.rows + 1
    172             self.selectedColumn = 1
    173         end
    174 
    175         if relColumn > 0 then
    176             self.selectedRow = 1
    177             self.selectedColumn = 0
    178         elseif relColumn < 0 then
    179             self.selectedRow = 1
    180             self.selectedColumn = self.columns + 1
    181         end
     178    if self.selectedRow > 0 or self.selectedColumn > 0 then
     179        self:setButtonStateNormal()
    182180    else
    183         self:setButtonStateNormal()
     181        if relMove > 0 then
     182            self[selectedThis] = 0
     183            self[selectedOther] = 1
     184        elseif relMove < 0 then
     185            self[selectedThis] = self[limitThis] + 1
     186            self[selectedOther] = 1
     187        else
     188            return
     189        end
    184190    end
    185191
    186192    -- move the selection according to the parameters
    187     self.selectedRow = self.selectedRow + relRow
    188     self.selectedColumn = self.selectedColumn + relColumn
    189 
    190     -- wrap around on overflow
    191     while self.selectedRow > self.rows do
    192         self.selectedRow = self.selectedRow - self.rows
    193     end
    194     while self.selectedColumn > self.columns do
    195         self.selectedColumn = self.selectedColumn - self.columns
    196     end
    197 
    198     -- wrap around on underflow
    199     while self.selectedRow <= 0 do
    200         self.selectedRow = self.selectedRow + self.rows
    201     end
    202     while self.selectedColumn <= 0 do
    203         self.selectedColumn = self.selectedColumn + self.columns
    204     end
    205 
    206     -- if the button is deactivated, call this function again
     193    self[selectedThis] = self[selectedThis] + relMove
     194
     195    -- wrap around on overflow or underflow
     196    while self[selectedThis] > self[limitThis] do self[selectedThis] = self[selectedThis] - self[limitThis] end
     197    while self[selectedThis] <= 0              do self[selectedThis] = self[selectedThis] + self[limitThis] end
     198
     199    -- if the button is deactivated, search the button closest to the desired location
    207200    if self:getSelectedButton() == nil then
    208         self:moveSelection(relRow, relColumn)
    209     else
     201        local min = self.rows + self.columns * self.ratio
     202        local minV1, minV2
     203        local limit, step
     204
     205        if relMove > 0 then
     206            limit = self[limitThis]
     207            step = 1
     208        else
     209            limit = 1
     210            step = -1
     211        end
     212
     213        for v1 = self[selectedThis], limit, step do
     214            for v2 = 1, self[limitOther] do
     215                local button
     216                if isRow == true then
     217                    button = self:getButton(v1, v2)
     218                else
     219                    button = self:getButton(v2, v1)
     220                end
     221                if button then
     222                    local distance
     223                    if isRow == true then
     224                        distance = math.sqrt((self[selectedThis] - v1)^2 + ((self[selectedOther] - v2) * self.ratio)^2)
     225                    else
     226                        distance = math.sqrt(((self[selectedThis] - v1) * self.ratio)^2 + (self[selectedOther] - v2)^2)
     227                    end
     228                    if distance < min then
     229                        min = distance; minV1 = v1; minV2 = v2
     230                    end
     231                end
     232            end
     233        end
     234
     235        if minV1 and minV2 then
     236            self[selectedThis] = minV1
     237            self[selectedOther] = minV2
     238        elseif self:hasButtons() then
     239            -- no suitable button found - wrap around and search again
     240            if relMove > 0 then
     241                self[selectedThis] = 0
     242            else
     243                self[selectedThis] = self[limitThis] + 1
     244            end
     245            self:moveSelection(relMove, selectedThis, selectedOther, limitThis, limitOther, isRow)
     246        end
     247    end
     248
     249    if self:hasSelection() == true then
    210250        self:setButtonStateSelected()
    211251    end
     
    222262end
    223263
     264-- Checks if there's at least one button in the table
     265function P:hasButtons()
     266    local count = 0
     267    for r = 1, self.rows do
     268        for c = 1, self.columns do
     269            if self:getButton(r, c) then
     270                count = count + 1
     271            end
     272        end
     273    end
     274
     275    return (count > 0)
     276end
     277
    224278-- Determines if a button is selected
    225279function P:hasSelection()
    226     if self.selectedRow == 0 or self.selectedColumn == 0 then
     280    if self.selectedRow and self.selectedRow > 0 and self.selectedColumn and self.selectedColumn > 0 then
     281        return true
     282    else
    227283        return false
    228     else
    229         return true
    230284    end
    231285end
  • code/branches/usability/data/gui/scripts/MultiplayerMenu.lua

    r7924 r7926  
    1111
    1212    --button are arranged in a 2x2 matrix, the lower items are both the back button
    13     P:initButtons(2, 2)
     13    P:initButtons(2, 3)
    1414
    1515    P:setButton(1, 1, {
     
    2323    })
    2424
    25     P:setButton(2, 1, {
     25    P:setButton(2, 3, {
    2626            ["button"] = winMgr:getWindow("orxonox/MultiplayerBackButton"),
    2727            ["callback"]  = P.MultiplayerBackButton_clicked
    2828    })
    29 
    30     P:setButton(2, 2, P:getButton(2, 1))
    3129end
    3230
  • code/branches/usability/data/gui/scripts/SheetManager.lua

    r7925 r7926  
    134134    -- select first button if the menu was opened with the keyboard
    135135    if previous and previous.pressedEnter and menuSheet.buttons and menuSheet:hasSelection() == false then
    136         menuSheet:moveSelection(1, 0)
     136        menuSheet:moveSelectionRow(1)
    137137    end
    138138
Note: See TracChangeset for help on using the changeset viewer.