Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/data/gui/scripts/GUISheet.lua @ 11817

Last change on this file since 11817 was 8862, checked in by landauf, 13 years ago

added config value to enable/disable preloading of menu sheets during startup (fast menus vs fast startup)

  • Property svn:eol-style set to native
File size: 12.0 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 and orxonox.GUIManager:preloadMenuSheets() 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.