Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/tutorial/data/gui/scripts/GUISheet.lua @ 9135

Last change on this file since 9135 was 8051, checked in by dafrick, 14 years ago

Merging latest changes in usability branch into tutorial branch.

  • Property svn:eol-style set to native
File size: 11.9 KB
RevLine 
[6718]1-- GUISheet.lua
[5491]2
3local P = {}
[6718]4_G[_REQUIREDNAME or "GUISheet"] = P
5P.__index = P
[5491]6
[6718]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
[5491]12end
13
[6718]14-- Override this function if you need to do work on load
[6720]15function P:onLoad()
[5523]16end
17
[7922]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
[7927]26-- Override this function if you need to do work on quit
27function P:onQuit()
[7922]28end
29
[8051]30-- Override this function if you want to react on keystrokes
31function P:onKeyPressed(mode)
32end
33
34-- Override this function if you want to update the gui after the window was resized
35function P:onWindowResized()
36end
37
[6747]38-- show function for the GUI
39function P:show()
40    self.window:show()
41    self.bVisible = true
42
[7922]43    -- set the selected button's state
[7928]44    self:setSelectedButtonsStateToSelected()
[7922]45
[6747]46    self:onShow()
47end
48
[5491]49-- hide function for the GUI
[6595]50function P:hide()
[5491]51    self.window:hide()
[6718]52    self.bVisible = false
[6747]53
54    self:onHide()
[5491]55end
56
[7927]57function P:quit()
[7922]58    -- reset the selected button
59    if self.buttons then
60        self:resetSelection()
61    end
[5491]62
[7927]63    self:onQuit()
[7403]64end
65
[6595]66function P:load()
[6718]67    -- Load the layout that describes the sheet
[6704]68    self.window = winMgr:loadWindowLayout(self.name .. ".layout")
[6718]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
[6720]75    self:onLoad()
[6748]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
[5559]83    return self
[5491]84end
85
[7922]86-- Handles key pressed while the gui sheed is displayed
[8051]87function P:keyPressed(mode)
[7922]88    if self.buttons then
[8051]89        if mode == "down" then     -- key down
[7926]90            self:moveSelectionRow(1)
[8051]91        elseif mode == "up" then -- key up
[7926]92            self:moveSelectionRow(-1)
[8051]93        elseif mode == "right" then -- key right
[7926]94            self:moveSelectionColumn(1)
[8051]95        elseif mode == "left" then -- key left
[7926]96            self:moveSelectionColumn(-1)
[8051]97        elseif mode == "enter" then -- key enter or key numpad enter
[7922]98            self:pressSelectedButton()
99        end
100    end
101
[8051]102    self:onKeyPressed(mode)
[7689]103end
104
[8051]105function P:windowResized()
106    self:onWindowResized()
[7922]107end
108
109
110-------------------------------------------------------------------------------
111-- Keyboard control -----------------------------------------------------------
112-------------------------------------------------------------------------------
113
114-- Initializes the buttons table, used to control the menu with the keyboard
[7928]115function P:initButtons(rows, columns)
[7922]116    self.rows = rows
117    self.columns = columns
118    self.buttons = {}
119    self.selectedRow = 0
120    self.selectedColumn = 0
[7928]121    self.ratio = 1
122end
[7926]123
[7928]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
[7922]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)
[7928]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)
[7922]138
[7928]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
[7922]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)
[7928]158    if self.buttons then
159        return self.buttons[(row - 1) * self.columns + (column - 1)]
160    else
161        return nil
162    end
[7922]163end
164
165-- Returns the selected button
166function P:getSelectedButton()
[7928]167    if self:hasSelection() then
168        return self:getButton(self.selectedRow, self.selectedColumn)
169    else
170        return nil
171    end
[7922]172end
173
174-- Presses the selected button if any
175function P:pressSelectedButton()
[7928]176    if self:getSelectedButton() then
[7925]177        self.pressedEnter = true
[7922]178        self:getSelectedButton().callback()
[7925]179        self.pressedEnter = false
[7922]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)
[7928]185    if not self.buttons then
186        return
187    end
188
[7922]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
[7928]191    self:setSelectedButtonsStateToNormal()
[7922]192
193    self.selectedRow = row
194    self.selectedColumn = column
195
[7928]196    self:setSelectedButtonsStateToSelected()
[7922]197end
198
[7928]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
[7926]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)
[7928]244    if not self.buttons then
245        return
246    end
247
[7922]248    -- if there's no selection yet, prepare it such that the selection enters the table from the desired side
[7926]249    if self.selectedRow > 0 or self.selectedColumn > 0 then
[7928]250        self:setSelectedButtonsStateToNormal()
[7926]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
[7922]260        end
261    end
262
263    -- move the selection according to the parameters
[7926]264    self[selectedThis] = self[selectedThis] + relMove
[7922]265
[7926]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
[7922]269
[7926]270    -- if the button is deactivated, search the button closest to the desired location
271    if self:getSelectedButton() == nil then
[7928]272        local min = 1000000
[7926]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
[7922]318    end
319
[7928]320    self:setSelectedButtonsStateToSelected()
[7922]321end
322
323-- Resets the selection
324function P:resetSelection()
[7928]325    self:setSelectedButtonsStateToNormal()
[7922]326
327    self.selectedRow = 0
328    self.selectedColumn = 0
329end
330
[7926]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
[7922]345-- Determines if a button is selected
346function P:hasSelection()
[7926]347    if self.selectedRow and self.selectedRow > 0 and self.selectedColumn and self.selectedColumn > 0 then
348        return true
349    else
[7922]350        return false
351    end
352end
353
354-- Sets the selected button's state to normal
[7928]355function P:setSelectedButtonsStateToNormal()
356    self:setSelectedButtonsState("Normal")
[7922]357end
358
359-- Sets the selected button's state to selected
[7928]360function P:setSelectedButtonsStateToSelected()
361    self:setSelectedButtonsState("Selected")
[7922]362end
363
364-- Sets the selected button's state to pushed
[7928]365function P:setSelectedButtonsStateToPushed()
366    self:setSelectedButtonsState("Pushed")
[7922]367end
368
369-- Sets the selected button's state
[7928]370function P:setSelectedButtonsState(state)
[7922]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
[5661]398return P
Note: See TracBrowser for help on using the repository browser.