/ld26

To get this branch, use:
bzr branch /bzr/ld26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
-- Class: TextInput
-- This is like a <Text> object, only it listens to user input and
-- adjusts its text property accordingly.
--
-- Extends:
--		<Text>
--
-- Events:
--		onChange - Occurs when the input's text has changed, either by a
--				   user's input or programmtically.
--		onType - Occurs when the input is about to accept input from a key.
--				 This event handler is passed the key about to be inserted.
--				 If the handler returns false, *not* nil or any other value,
--				 then the key is ignored.

TextInput = Text:extend{
	text = '',

	-- Property: listening
	-- Whether the input is currently listening to input.
	listening = true,

	-- Property: caret
	-- This shows the current insert position.
	caret = 0,

	-- Property: blinkRate
	-- How quickly the caret blinks, in seconds.
	blinkRate = 0.5,

	-- internal property: _blinkTimer
	-- Used to keep track of caret blinking.
	_blinkTimer = 0,

	-- internal property: _repeatKey
	-- Used to keep track of what movement key is being held down.
	
	-- internal property: _repeatTimer
	-- Used to keep track of how quickly movement keys repeat.

	-- internal property: _caretHeight
	-- How tall the caret is onscreen, based on the font.

	-- internal property: _caretX
	-- Where to draw the caret, relative to the sprite's x position.

	update = function (self, elapsed)
		if self.listening then
			-- listen to normal keys

			if the.keys.typed ~= '' then
				if (self.onType and self:onType(the.keys.typed)) or not self.onType then
					self.text = string.sub(self.text, 1, self.caret) .. the.keys.typed
								.. string.sub(self.text, self.caret + 1)
					self.caret = self.caret + string.len(the.keys.typed)
				end
			end

			if the.keys:justPressed('home') then
				self.caret = 0
			end

			if the.keys:justPressed('end') then
				self.caret = string.len(self.text)
			end

			-- handle movement keys that repeat
			-- we have to simulate repeat rates manually :(

			local delay, rate = love.keyboard.getKeyRepeat()
			local frameAction

			for _, key in pairs{'backspace', 'delete', 'left', 'right'} do
				if the.keys:pressed(key) then
					if self._repeatKey == key then
						self._repeatTimer = self._repeatTimer + elapsed
						
						-- if we've made it past the maximum delay, then
						-- we reset the timer and take action

						if self._repeatTimer > delay + rate then
							self._repeatTimer = delay
							frameAction = key
						end
					else
						-- we've just started holding down the key

						self._repeatKey = key
						self._repeatTimer = 0
						frameAction = key
					end
				else
					if self._repeatKey == key then
						self._repeatKey = nil
					end
				end
			end

			if frameAction == 'backspace' and self.caret > 0 then
				self.text = string.sub(self.text, 1, self.caret - 1) .. string.sub(self.text, self.caret + 1)
				self.caret = self.caret - 1
			end

			if frameAction == 'delete' and self.caret < string.len(self.text) then
				self.text = string.sub(self.text, 1, self.caret) .. string.sub(self.text, self.caret + 2)
			end

			if frameAction == 'left' and self.caret > 0 then
				self.caret = self.caret - 1
			end

			if frameAction == 'right' and self.caret < string.len(self.text) then
				self.caret = self.caret + 1
			end
		end

		-- update caret position

		if self._set.caret ~= self.caret and self._fontObj then
			self._caretX = self._fontObj:getWidth(string.sub(self.text, 1, self.caret))
			self._caretHeight = self._fontObj:getHeight()
			self._set.caret = self.caret
		end

		-- update caret timer

		self._blinkTimer = self._blinkTimer + elapsed
		if self._blinkTimer > self.blinkRate * 2 then self._blinkTimer = 0 end

		-- call onChange handler as needed

		if self.text ~= self._set.text then
			if self.onChange then self:onChange() end
			self._set.text = self.text
		end

		Text.update(self, elapsed)
	end,

	draw = function (self, x, y)
		if self.visible then
			x = x or self.x
			y = y or self.y

			Text.draw(self, x, y)

			-- draw caret
			
			if (self._repeatKey or self._blinkTimer < self.blinkRate) and
			   (self._caretX and self._caretHeight) then
				love.graphics.setLineWidth(1)
				love.graphics.line(x + self._caretX, y, x + self._caretX, y + self._caretHeight)
			end
		end
	end
}