Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/spacerace/data/gui/scripts/GUISheet.lua @ 8703

Last change on this file since 8703 was 8079, checked in by landauf, 14 years ago

merged usability branch back to trunk

incomplete summary of the changes in this branch:

  • enhanced keyboard navigation in GUIs
  • implemented new graphics menu and changeable window size at runtime
  • added developer mode
  • HUD shows if game is paused, game pauses if ingame menu is opened
  • removed a few obsolete commands and hid some that are more for internal use
  • numpad works in console and gui
  • faster loading of level info
  • enhanced usage of compositors (Shader class)
  • improved camera handling, configurable FOV and aspect ratio
  • Property svn:eol-style set to native
File size: 11.9 KB
Line 
1-- GUISheet.lua
2
3local P = {}
4_G[_REQUIREDNAME or "GUISheet"] = P
5P.__index = P
6
7-- Don't use directly --> use HUDSheet.new or MenuSheet.new
8function P.new(_name)
9    local newSheet = { name = _name }
10    setmetatable(newSheet, P)
11    return newSheet
12end
13
14-- Override this function if you need to do work on load
15function P:onLoad()
16end
17
18-- Override this function if you need to do work on show
19function P:onShow()
20end
21
22-- Override this function if you need to do work on hide
23function P:onHide()
24end
25
26-- Override this function if you need to do work on quit
27function P:onQuit()
28end
29
30-- Override this function if you want to react on keystrokes
31function P:onKeyPressed()
32end
33
34-- Override this function if you want to update the gui after the window was resized
35function P:onWindowResized()
36end
37
38-- show function for the GUI
39function P:show()
40    self.window:show()
41    self.bVisible = true
42
43    -- set the selected button's state
44    self:setSelectedButtonsStateToSelected()
45
46    self:onShow()
47end
48
49-- hide function for the GUI
50function P:hide()
51    self.window:hide()
52    self.bVisible = false
53
54    self:onHide()
55end
56
57function P:quit()
58    -- reset the selected button
59    if self.buttons then
60        self:resetSelection()
61    end
62
63    self:onQuit()
64end
65
66function P:load()
67    -- Load the layout that describes the sheet
68    self.window = winMgr:loadWindowLayout(self.name .. ".layout")
69    if self.window == nil then
70        error("Could not load layout file for GUI sheet '"..self.name.."'")
71    end
72    -- Hide it at first
73    self:hide()
74    -- Allow sheets to do some work upon loading
75    self:onLoad()
76
77    -- Also load additional sheets to avoid display lags
78    if self.loadAlong then
79        for k, sheet in pairs(self.loadAlong) do
80            loadSheet(sheet)
81        end
82    end
83    return self
84end
85
86-- Handles key pressed while the gui sheed is displayed
87function P:keyPressed()
88    if self.buttons then
89        if code == "208" then     -- key down
90            self:moveSelectionRow(1)
91        elseif code == "200" then -- key up
92            self:moveSelectionRow(-1)
93        elseif code == "205" then -- key right
94            self:moveSelectionColumn(1)
95        elseif code == "203" then -- key left
96            self:moveSelectionColumn(-1)
97        elseif code == "28" or code == "156"  then -- key enter or key numpad enter
98            self:pressSelectedButton()
99        end
100    end
101
102    self:onKeyPressed()
103end
104
105function P:windowResized()
106    self:onWindowResized()
107end
108
109
110-------------------------------------------------------------------------------
111-- Keyboard control -----------------------------------------------------------
112-------------------------------------------------------------------------------
113
114-- Initializes the buttons table, used to control the menu with the keyboard
115function P:initButtons(rows, columns)
116    self.rows = rows
117    self.columns = columns
118    self.buttons = {}
119    self.selectedRow = 0
120    self.selectedColumn = 0
121    self.ratio = 1
122end
123
124-- 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)
125function P:setRatio(ratio)
126    self.ratio = ratio
127end
128
129-- Defines the button for a given position in the table. The upper-left button is at position (1, 1)
130function P:setButton(row, column, button)
131    if not self.buttons then
132        -- init the table
133        self:initButtons(row, column)
134    elseif row > self.rows or column > self.columns then
135        -- rearrange the table
136        local maxRows = math.max(self.rows, row)
137        local maxColumns = math.max(self.columns, column)
138
139        for r = self.rows, 1, -1 do
140            for c = self.columns, 1, -1 do
141                local b = self:getButton(r, c)
142                if b then
143                    self.buttons[(r - 1) * self.columns + (c - 1)] = nil
144                    self.buttons[(r - 1) * maxColumns + (c - 1)] = b
145                end
146            end
147        end
148
149        self.rows = maxRows
150        self.columns = maxColumns
151    end
152
153    self.buttons[(row - 1) * self.columns + (column - 1)] = button
154end
155
156-- Returns the button at a given position in the table. The upper-left button is at position (1, 1)
157function P:getButton(row, column)
158    if self.buttons then
159        return self.buttons[(row - 1) * self.columns + (column - 1)]
160    else
161        return nil
162    end
163end
164
165-- Returns the selected button
166function P:getSelectedButton()
167    if self:hasSelection() then
168        return self:getButton(self.selectedRow, self.selectedColumn)
169    else
170        return nil
171    end
172end
173
174-- Presses the selected button if any
175function P:pressSelectedButton()
176    if self:getSelectedButton() then
177        self.pressedEnter = true
178        self:getSelectedButton().callback()
179        self.pressedEnter = false
180    end
181end
182
183-- Sets the selection to a given row and column. The upper-left button is at position (1, 1)
184function P:setSelection(row, column)
185    if not self.buttons then
186        return
187    end
188
189    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 .. ")")
190
191    self:setSelectedButtonsStateToNormal()
192
193    self.selectedRow = row
194    self.selectedColumn = column
195
196    self:setSelectedButtonsStateToSelected()
197end
198
199-- Sets the selection to the button closest to the given row and column. The upper-left button is at position (1, 1)
200function P:setSelectionNear(row, column)
201    if not self.buttons then
202        return
203    end
204
205    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 .. ")")
206
207    if self:getButton(row, column) then
208        self:setSelection(row, column)
209    else
210        local min = 1000000
211        local minRow, minColumn
212
213        for r = 1, self.rows do
214            for c = 1, self.columns do
215                if self:getButton(r, c) then
216                    local distance = math.sqrt((row - r)^2 + ((column - c) * self.ratio)^2)
217                    if distance < min then
218                        min = distance; minRow = r; minColumn = c
219                    end
220                end
221            end
222        end
223
224        if minRow and minColumn then
225            self:setSelection(minRow, minColumn)
226        else
227            self:resetSelection()
228        end
229    end
230end
231
232-- Moves the selection by a given number of rows (a positive value means down, a negative value means up)
233function P:moveSelectionRow(relRow)
234    self:moveSelection(relRow, "selectedRow", "selectedColumn", "rows", "columns", true)
235end
236
237-- Moves the selection by a given number of columns (a positive value means right, a negative value means left)
238function P:moveSelectionColumn(relColumn)
239    self:moveSelection(relColumn, "selectedColumn", "selectedRow", "columns", "rows", false)
240end
241
242-- Generic move function, the values are determined at runtime depending on the arguments
243function P:moveSelection(relMove, selectedThis, selectedOther, limitThis, limitOther, isRow)
244    if not self.buttons then
245        return
246    end
247
248    -- if there's no selection yet, prepare it such that the selection enters the table from the desired side
249    if self.selectedRow > 0 or self.selectedColumn > 0 then
250        self:setSelectedButtonsStateToNormal()
251    else
252        if relMove > 0 then
253            self[selectedThis] = 0
254            self[selectedOther] = 1
255        elseif relMove < 0 then
256            self[selectedThis] = self[limitThis] + 1
257            self[selectedOther] = 1
258        else
259            return
260        end
261    end
262
263    -- move the selection according to the parameters
264    self[selectedThis] = self[selectedThis] + relMove
265
266    -- wrap around on overflow or underflow
267    while self[selectedThis] > self[limitThis] do self[selectedThis] = self[selectedThis] - self[limitThis] end
268    while self[selectedThis] <= 0              do self[selectedThis] = self[selectedThis] + self[limitThis] end
269
270    -- if the button is deactivated, search the button closest to the desired location
271    if self:getSelectedButton() == nil then
272        local min = 1000000
273        local minV1, minV2
274        local limit, step
275
276        if relMove > 0 then
277            limit = self[limitThis]
278            step = 1
279        else
280            limit = 1
281            step = -1
282        end
283
284        for v1 = self[selectedThis], limit, step do
285            for v2 = 1, self[limitOther] do
286                local button
287                if isRow == true then
288                    button = self:getButton(v1, v2)
289                else
290                    button = self:getButton(v2, v1)
291                end
292                if button then
293                    local distance
294                    if isRow == true then
295                        distance = math.sqrt((self[selectedThis] - v1)^2 + ((self[selectedOther] - v2) * self.ratio)^2)
296                    else
297                        distance = math.sqrt(((self[selectedThis] - v1) * self.ratio)^2 + (self[selectedOther] - v2)^2)
298                    end
299                    if distance < min then
300                        min = distance; minV1 = v1; minV2 = v2
301                    end
302                end
303            end
304        end
305
306        if minV1 and minV2 then
307            self[selectedThis] = minV1
308            self[selectedOther] = minV2
309        elseif self:hasButtons() then
310            -- no suitable button found - wrap around and search again
311            if relMove > 0 then
312                self[selectedThis] = 0
313            else
314                self[selectedThis] = self[limitThis] + 1
315            end
316            self:moveSelection(relMove, selectedThis, selectedOther, limitThis, limitOther, isRow)
317        end
318    end
319
320    self:setSelectedButtonsStateToSelected()
321end
322
323-- Resets the selection
324function P:resetSelection()
325    self:setSelectedButtonsStateToNormal()
326
327    self.selectedRow = 0
328    self.selectedColumn = 0
329end
330
331-- Checks if there's at least one button in the table
332function P:hasButtons()
333    local count = 0
334    for r = 1, self.rows do
335        for c = 1, self.columns do
336            if self:getButton(r, c) then
337                count = count + 1
338            end
339        end
340    end
341
342    return (count > 0)
343end
344
345-- Determines if a button is selected
346function P:hasSelection()
347    if self.selectedRow and self.selectedRow > 0 and self.selectedColumn and self.selectedColumn > 0 then
348        return true
349    else
350        return false
351    end
352end
353
354-- Sets the selected button's state to normal
355function P:setSelectedButtonsStateToNormal()
356    self:setSelectedButtonsState("Normal")
357end
358
359-- Sets the selected button's state to selected
360function P:setSelectedButtonsStateToSelected()
361    self:setSelectedButtonsState("Selected")
362end
363
364-- Sets the selected button's state to pushed
365function P:setSelectedButtonsStateToPushed()
366    self:setSelectedButtonsState("Pushed")
367end
368
369-- Sets the selected button's state
370function P:setSelectedButtonsState(state)
371    if self:getSelectedButton() then
372        local element = self:getSelectedButton().button
373        local offset = getElementStateOffset(element)
374
375        if offset then
376            element:setProperty("NormalImageRightEdge",  string.sub(element:getProperty("NormalImageRightEdge"),  1, offset) .. state)
377            element:setProperty("NormalImageLeftEdge",   string.sub(element:getProperty("NormalImageLeftEdge"),   1, offset) .. state)
378            element:setProperty("NormalImageBackground", string.sub(element:getProperty("NormalImageBackground"), 1, offset) .. state)
379        end
380    end
381end
382
383-- Gets the offset of the button's current state
384function getElementStateOffset(element)
385    local property = element:getProperty("NormalImageRightEdge")
386
387    if string.sub(property, string.len(property) - 5, string.len(property)) == "Normal" then
388        return -7
389    elseif string.sub(property, string.len(property) - 7, string.len(property)) == "Selected" then
390        return -9
391    elseif string.sub(property, string.len(property) - 5, string.len(property)) == "Pushed" then
392        return -7
393    else
394        return nil
395    end
396end
397
398return P
Note: See TracBrowser for help on using the repository browser.