· 5 years ago · Jan 19, 2021, 08:54 PM
1--Bedrock Build: 582
2--This code is squished down in to one, rather hard to read file.
3--As such it is not much good for anything other than being loaded as an API.
4--If you want to look at the code to learn from it, copy parts or just take a look,
5--you should go to the GitHub repo. http://github.com/oeed/Bedrock/
6
7--
8-- Bedrock is the core program framework used by all OneOS and OneCode programs.
9-- Inspired by Apple's Cocoa framework.
10-- (c) oeed 2014
11--
12-- For documentation see the Bedrock wiki, github.com/oeed/Bedrock/wiki/
13--
14
15local apis = {
16["Communicate"] = [[
17Channel = 42000
18Callbacks = {}
19CallbackTimeouts = {}
20MessageTimeout = 0.5
21MessageTypeHandlers = {}
22
23local getNames = peripheral.getNames or function()
24 local tResults = {}
25 for n,sSide in ipairs( rs.getSides() ) do
26 if peripheral.isPresent( sSide ) then
27 table.insert( tResults, sSide )
28 local isWireless = false
29 if pcall(function()isWireless = peripheral.call(sSide, 'isWireless') end) then
30 isWireless = true
31 end
32 if peripheral.getType( sSide ) == "modem" and not isWireless then
33 local tRemote = peripheral.call( sSide, "getNamesRemote" )
34 for n,sName in ipairs( tRemote ) do
35 table.insert( tResults, sName )
36 end
37 end
38 end
39 end
40 return tResults
41end
42
43GetModems = function(self, callback)
44 local ok = false
45 for i, name in ipairs(getNames()) do
46 ok = true
47 if callback then
48 local p = peripheral.wrap(name)
49 callback(p, name)
50 end
51 end
52 return ok
53end
54
55Initialise = function(self, bedrock)
56 local new = {}
57 setmetatable(new, {__index = self})
58 new.Bedrock = bedrock
59 new.Bedrock:RegisterEvent('modem_message', function(_, event, name, channel, replyChannel, message, distance)
60 new:OnMessage(event, name, channel, replyChannel, message, distance)
61 end)
62 if new:GetModems(function(modem, name)
63 modem.open(new.Channel)
64 end)
65 then
66 return new
67 end
68 return false
69end
70
71RegisterMessageType = function(self, msgType, callback)
72 self.MessageTypeHandlers[msgType] = callback
73end
74
75OnMessage = function(self, event, name, channel, replyChannel, message, distance)
76 if channel == self.Channel and type(message) == 'table' and message.msgType and message.thread and message.id ~= os.getComputerID() then
77 if self.Callbacks[message.thread] then
78 local response, callback = self.Callbacks[message.thread](message.content, message, distance)
79 if response ~= nil then
80 self:Reply(response, message, callback)
81 end
82 elseif self.MessageTypeHandlers[message.msgType] then
83 local response = self.MessageTypeHandlers[message.msgType](message, message.msgType, message.content, distance)
84 if response ~= nil then
85 self:Reply(response, message)
86 end
87 else
88 end
89 return true
90 else
91 return false
92 end
93end
94
95Reply = function(self, content, message, callback)
96 self:SendMessage(message.msgType, content, callback, message.thread)
97end
98
99SendMessage = function(self, msgType, content, callback, thread, multiple)
100 thread = thread or tostring(math.random())
101 if self:GetModems(function(modem, name)
102 modem.transmit(self.Channel, self.Channel, {msgType = msgType, content = content, thread = thread, id = os.getComputerID()})
103 end)
104 then
105 if callback then
106 self.Callbacks[thread] = function(...)
107 if not multiple then
108 self.Callbacks[thread] = nil
109 self.CallbackTimeouts[thread] = nil
110 end
111 return callback(...), callback
112 end
113 self.CallbackTimeouts[thread] = self.Bedrock:StartTimer(function(_, timer)
114 if timer == self.CallbackTimeouts[thread] then
115 callback(false)
116 end
117 end, self.MessageTimeout)
118 end
119 return true
120 end
121 return false
122end
123]],
124["Drawing"] = [[
125local round = function(num, idp)
126 local mult = 10^(idp or 0)
127 return math.floor(num * mult + 0.5) / mult
128end
129
130local _w, _h = term.getSize()
131local copyBuffer = nil
132
133Screen = {
134 Width = _w,
135 Height = _h
136}
137
138Constraints = {
139
140}
141
142CurrentConstraint = {1,1,_w,_h}
143IgnoreConstraint = false
144
145function AddConstraint(x, y, width, height)
146 local x2 = x + width - 1
147 local y2 = y + height - 1
148 table.insert(Constraints, {x, y, x2, y2})
149 GetConstraint()
150end
151
152function RemoveConstraint()
153 --table.remove(Constraints, #Constraints)
154 Constraints[#Constraints] = nil
155 GetConstraint()
156end
157
158function GetConstraint()
159 local x = 1
160 local y = 1
161 local x2 = Screen.Width
162 local y2 = Screen.Height
163 for i, c in ipairs(Constraints) do
164 if x < c[1] then
165 x = c[1]
166 end
167 if y < c[2] then
168 y = c[2]
169 end
170 if x2 > c[3] then
171 x2 = c[3]
172 end
173 if y2 > c[4] then
174 y2 = c[4]
175 end
176 end
177 CurrentConstraint = {x, y, x2, y2}
178end
179
180function WithinContraint(x, y)
181 return IgnoreConstraint or
182 (x >= CurrentConstraint[1] and
183 y >= CurrentConstraint[2] and
184 x <= CurrentConstraint[3] and
185 y <= CurrentConstraint[4])
186end
187
188colours.transparent = 0
189colors.transparent = 0
190
191Filters = {
192 None = {
193 [colours.white] = colours.white,
194 [colours.orange] = colours.orange,
195 [colours.magenta] = colours.magenta,
196 [colours.lightBlue] = colours.lightBlue,
197 [colours.yellow] = colours.yellow,
198 [colours.lime] = colours.lime,
199 [colours.pink] = colours.pink,
200 [colours.grey] = colours.grey,
201 [colours.lightGrey] = colours.lightGrey,
202 [colours.cyan] = colours.cyan,
203 [colours.purple] = colours.purple,
204 [colours.blue] = colours.blue,
205 [colours.brown] = colours.brown,
206 [colours.green] = colours.green,
207 [colours.red] = colours.red,
208 [colours.black] = colours.black,
209 [colours.transparent] = colours.transparent,
210 },
211
212 Greyscale = {
213 [colours.white] = colours.white,
214 [colours.orange] = colours.lightGrey,
215 [colours.magenta] = colours.lightGrey,
216 [colours.lightBlue] = colours.lightGrey,
217 [colours.yellow] = colours.lightGrey,
218 [colours.lime] = colours.lightGrey,
219 [colours.pink] = colours.lightGrey,
220 [colours.grey] = colours.grey,
221 [colours.lightGrey] = colours.lightGrey,
222 [colours.cyan] = colours.grey,
223 [colours.purple] = colours.grey,
224 [colours.blue] = colours.grey,
225 [colours.brown] = colours.grey,
226 [colours.green] = colours.grey,
227 [colours.red] = colours.grey,
228 [colours.black] = colours.black,
229 [colours.transparent] = colours.transparent,
230 },
231
232 BlackWhite = {
233 [colours.white] = colours.white,
234 [colours.orange] = colours.white,
235 [colours.magenta] = colours.white,
236 [colours.lightBlue] = colours.white,
237 [colours.yellow] = colours.white,
238 [colours.lime] = colours.white,
239 [colours.pink] = colours.white,
240 [colours.grey] = colours.black,
241 [colours.lightGrey] = colours.white,
242 [colours.cyan] = colours.black,
243 [colours.purple] = colours.black,
244 [colours.blue] = colours.black,
245 [colours.brown] = colours.black,
246 [colours.green] = colours.black,
247 [colours.red] = colours.black,
248 [colours.black] = colours.black,
249 [colours.transparent] = colours.transparent,
250 },
251
252 Darker = {
253 [colours.white] = colours.lightGrey,
254 [colours.orange] = colours.red,
255 [colours.magenta] = colours.purple,
256 [colours.lightBlue] = colours.cyan,
257 [colours.yellow] = colours.orange,
258 [colours.lime] = colours.green,
259 [colours.pink] = colours.magenta,
260 [colours.grey] = colours.black,
261 [colours.lightGrey] = colours.grey,
262 [colours.cyan] = colours.blue,
263 [colours.purple] = colours.grey,
264 [colours.blue] = colours.grey,
265 [colours.brown] = colours.grey,
266 [colours.green] = colours.grey,
267 [colours.red] = colours.brown,
268 [colours.black] = colours.black,
269 [colours.transparent] = colours.transparent,
270 },
271
272 Lighter = {
273 [colours.white] = colours.lightGrey,
274 [colours.orange] = colours.yellow,
275 [colours.magenta] = colours.pink,
276 [colours.lightBlue] = colours.cyan,
277 [colours.yellow] = colours.orange,
278 [colours.lime] = colours.green,
279 [colours.pink] = colours.magenta,
280 [colours.grey] = colours.lightGrey,
281 [colours.lightGrey] = colours.grey,
282 [colours.cyan] = colours.lightBlue,
283 [colours.purple] = colours.magenta,
284 [colours.blue] = colours.lightBlue,
285 [colours.brown] = colours.red,
286 [colours.green] = colours.lime,
287 [colours.red] = colours.orange,
288 [colours.black] = colours.grey,
289 [colours.transparent] = colours.transparent,
290 },
291
292 Highlight = {
293 [colours.white] = colours.lightGrey,
294 [colours.orange] = colours.yellow,
295 [colours.magenta] = colours.pink,
296 [colours.lightBlue] = colours.cyan,
297 [colours.yellow] = colours.orange,
298 [colours.lime] = colours.green,
299 [colours.pink] = colours.magenta,
300 [colours.grey] = colours.lightGrey,
301 [colours.lightGrey] = colours.grey,
302 [colours.cyan] = colours.lightBlue,
303 [colours.purple] = colours.magenta,
304 [colours.blue] = colours.lightBlue,
305 [colours.brown] = colours.red,
306 [colours.green] = colours.lime,
307 [colours.red] = colours.orange,
308 [colours.black] = colours.grey,
309 [colours.transparent] = colours.transparent,
310 },
311
312 Invert = {
313 [colours.white] = colours.black,
314 [colours.orange] = colours.blue,
315 [colours.magenta] = colours.green,
316 [colours.lightBlue] = colours.brown,
317 [colours.yellow] = colours.blue,
318 [colours.lime] = colours.purple,
319 [colours.pink] = colours.green,
320 [colours.grey] = colours.lightGrey,
321 [colours.lightGrey] = colours.grey,
322 [colours.cyan] = colours.red,
323 [colours.purple] = colours.green,
324 [colours.blue] = colours.yellow,
325 [colours.brown] = colours.lightBlue,
326 [colours.green] = colours.purple,
327 [colours.red] = colours.cyan,
328 [colours.black] = colours.white,
329 [colours.transparent] = colours.transparent,
330 },
331}
332
333function FilterColour(colour, filter)
334 if filter[colour] then
335 return filter[colour]
336 else
337 return colour
338 end
339end
340
341DrawCharacters = function (x, y, characters, textColour, bgColour)
342 WriteStringToBuffer(x, y, tostring(characters), textColour, bgColour)
343end
344
345DrawBlankArea = function (x, y, w, h, colour)
346 if colour ~= colours.transparent then
347 DrawArea(x, y, w, h, " ", 1, colour)
348 end
349end
350
351DrawArea = function (x, y, w, h, character, textColour, bgColour)
352 --width must be greater than 1, otherwise we get problems
353 if w < 0 then
354 w = w * -1
355 elseif w == 0 then
356 w = 1
357 end
358
359 for ix = 1, w do
360 local currX = x + ix - 1
361 for iy = 1, h do
362 local currY = y + iy - 1
363 WriteToBuffer(currX, currY, character, textColour, bgColour)
364 end
365 end
366end
367
368DrawImage = function(_x,_y,tImage, w, h)
369 if tImage then
370 for y = 1, h do
371 if not tImage[y] then
372 break
373 end
374 for x = 1, w do
375 if not tImage[y][x] then
376 break
377 end
378 local bgColour = tImage[y][x]
379 local textColour = tImage.textcol[y][x] or colours.white
380 local char = tImage.text[y][x]
381 WriteToBuffer(x+_x-1, y+_y-1, char, textColour, bgColour)
382 end
383 end
384 elseif w and h then
385 DrawBlankArea(_x, _y, w, h, colours.lightGrey)
386 end
387end
388
389--using .nft
390LoadImage = function(path, global)
391 local image = {
392 text = {},
393 textcol = {}
394 }
395 if fs.exists(path) then
396 local _io = io
397 if OneOS and global then
398 _io = OneOS.IO
399 end
400 local file = _io.open(path, "r")
401 if not file then
402 error('Error Occured. _io:'..tostring(_io)..' OneOS: '..tostring(OneOS)..' OneOS.IO'..tostring(OneOS.IO)..' io: '..tostring(io))
403 end
404 local sLine = file:read()
405 local num = 1
406 while sLine do
407 table.insert(image, num, {})
408 table.insert(image.text, num, {})
409 table.insert(image.textcol, num, {})
410
411 --As we're no longer 1-1, we keep track of what index to write to
412 local writeIndex = 1
413 --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour
414 local bgNext, fgNext = false, false
415 --The current background and foreground colours
416 local currBG, currFG = nil,nil
417 for i=1,#sLine do
418 local nextChar = string.sub(sLine, i, i)
419 if nextChar:byte() == 30 then
420 bgNext = true
421 elseif nextChar:byte() == 31 then
422 fgNext = true
423 elseif bgNext then
424 currBG = GetColour(nextChar)
425 if currBG == nil then
426 currBG = colours.transparent
427 end
428 bgNext = false
429 elseif fgNext then
430 currFG = GetColour(nextChar)
431 if currFG == nil or currFG == colours.transparent then
432 currFG = colours.white
433 end
434 fgNext = false
435 else
436 if nextChar ~= " " and currFG == nil then
437 currFG = colours.white
438 end
439 image[num][writeIndex] = currBG
440 image.textcol[num][writeIndex] = currFG
441 image.text[num][writeIndex] = nextChar
442 writeIndex = writeIndex + 1
443 end
444 end
445 num = num+1
446 sLine = file:read()
447 end
448 file:close()
449 else
450 return nil
451 end
452 return image
453end
454
455DrawCharactersCenter = function(x, y, w, h, characters, textColour,bgColour)
456 w = w or Screen.Width
457 h = h or Screen.Height
458 x = x or 0
459 y = y or 0
460 x = math.floor((w - #characters) / 2) + x
461 y = math.floor(h / 2) + y
462
463 DrawCharacters(x, y, characters, textColour, bgColour)
464end
465
466GetColour = function(hex)
467 if hex == ' ' then
468 return colours.transparent
469 end
470 local value = tonumber(hex, 16)
471 if not value then return nil end
472 value = math.pow(2,value)
473 return value
474end
475
476Clear = function (_colour)
477 _colour = _colour or colours.black
478 DrawBlankArea(1, 1, Screen.Width, Screen.Height, _colour)
479end
480
481Buffer = {}
482BackBuffer = {}
483
484local buffer = Buffer
485local backBuffer = BackBuffer
486
487TryRestore = false
488
489
490--TODO: make this quicker
491-- maybe sort the pixels in order of colour so it doesn't have to set the colour each time
492DrawBuffer = function()
493 if TryRestore and Restore then
494 Restore()
495 end
496
497 -- If the program is within OneOS pass our buffer straight to the OS to draw rather than fidling around with the term API
498
499 if OneOS and OneOS.Buffer then
500 buffer = OneOS.Buffer
501 else
502 for y,row in pairs(buffer) do
503 for x,pixel in pairs(row) do
504 local shouldDraw = true
505 local hasBackBuffer = true
506 if BackBuffer[y] == nil or BackBuffer[y][x] == nil or #BackBuffer[y][x] ~= 3 then
507 hasBackBuffer = false
508 end
509 if hasBackBuffer and BackBuffer[y][x][1] == buffer[y][x][1] and BackBuffer[y][x][2] == buffer[y][x][2] and BackBuffer[y][x][3] == buffer[y][x][3] then
510 shouldDraw = false
511 end
512 if shouldDraw then
513 term.setBackgroundColour(pixel[3])
514 term.setTextColour(pixel[2])
515 term.setCursorPos(x, y)
516 term.write(pixel[1])
517 end
518 end
519 end
520 end
521 BackBuffer = buffer
522 buffer = {}
523end
524
525ClearBuffer = function()
526 buffer = {}
527end
528
529WriteStringToBuffer = function (x, y, characters, textColour,bgColour)
530 for i = 1, #characters do
531 local character = characters:sub(i,i)
532 WriteToBuffer(x + i - 1, y, character, textColour, bgColour)
533 end
534end
535
536WriteToBuffer = function(x, y, character, textColour,bgColour, cached)
537 if not cached and not WithinContraint(x, y) then
538 return
539 end
540 x = round(x)
541 y = round(y)
542
543 if textColour == colours.transparent then
544 character = ' '
545 end
546
547 if bgColour == colours.transparent then
548 buffer[y] = buffer[y] or {}
549 buffer[y][x] = buffer[y][x] or {"", colours.white, colours.black}
550 buffer[y][x][1] = character
551 buffer[y][x][2] = textColour
552 else
553 buffer[y] = buffer[y] or {}
554 buffer[y][x] = {character, textColour, bgColour}
555 end
556
557 if copyBuffer then
558 copyBuffer[y] = copyBuffer[y] or {}
559 copyBuffer[y][x] = {character, textColour, bgColour}
560 end
561end
562
563DrawCachedBuffer = function(buffer)
564 for y, row in pairs(buffer) do
565 for x, pixel in pairs(row) do
566 WriteToBuffer(x, y, pixel[1], pixel[2], pixel[3], true)
567 end
568 end
569end
570
571StartCopyBuffer = function()
572 copyBuffer = {}
573end
574
575EndCopyBuffer = function()
576 local tmpCopy = copyBuffer
577 copyBuffer = nil
578 return tmpCopy
579end
580]],
581["Helpers"] = [[
582LongestString = function(input, key, isKey)
583 local length = 0
584 if isKey then
585 for k, v in pairs(input) do
586 local titleLength = string.len(k)
587 if titleLength > length then
588 length = titleLength
589 end
590 end
591 else
592 for i = 1, #input do
593 local value = input[i]
594 if key then
595 if value[key] then
596 value = value[key]
597 else
598 value = ''
599 end
600 end
601 local titleLength = string.len(value)
602 if titleLength > length then
603 length = titleLength
604 end
605 end
606 end
607 return length
608end
609
610Split = function(a,e)
611 local t,e=e or":",{}
612 local t=string.format("([^%s]+)",t)
613 a:gsub(t,function(t)e[#e+1]=t end)
614 return e
615end
616
617Extension = function(path, addDot)
618 if not path then
619 return nil
620 elseif not string.find(fs.getName(path), '%.') then
621 return ''
622 else
623 local _path = path
624 if path:sub(#path) == '/' then
625 _path = path:sub(1,#path-1)
626 end
627
628 local extension = _path:gmatch('%.[0-9a-z]+$')()
629 if extension then
630 extension = extension:sub(2)
631 elseif fs.getName(_path):sub(1,1) == '.' then
632 extension = fs.getName(_path):sub(2)
633 else
634 return ''
635 end
636
637 if addDot then
638 extension = '.'..extension
639 end
640 return extension:lower()
641 end
642end
643
644RemoveExtension = function(path)
645--local name = string.match(fs.getName(path), '(%a+)%.?.-')
646 if not path:find('%.') then
647 return path
648 end
649 local extension = Helpers.Extension(path)
650 if extension == path then
651 return fs.getName(path)
652 end
653 return string.gsub(path, extension, ''):sub(1, -2)
654end
655
656RemoveFileName = function(path)
657 if string.sub(path, -1) == '/' then
658 path = string.sub(path, 1, -2)
659 end
660 local v = string.match(path, "(.-)([^\\/]-%.?([^%.\\/]*))$")
661 if type(v) == 'string' then
662 return v
663 end
664 return v[1]
665end
666
667ParentFolder = function(path)
668 local folderName = fs.getName(path)
669 return path:sub(1, #path-#folderName-1)
670end
671
672TruncateString = function(sString, maxLength)
673 if #sString > maxLength then
674 sString = sString:sub(1,maxLength-3)
675 if sString:sub(-1) == ' ' then
676 sString = sString:sub(1,maxLength-4)
677 end
678 sString = sString .. '...'
679 end
680 return sString
681end
682
683TruncateStringStart = function(sString, maxLength)
684 local len = #sString
685 if #sString > maxLength then
686 sString = sString:sub(len - maxLength, len - 3)
687 if sString:sub(-1) == ' ' then
688 sString = sString:sub(len - maxLength, len - 4)
689 end
690 sString = '...' .. sString
691 end
692 return sString
693end
694
695WrapText = function(text, maxWidth)
696 local lines = {''}
697 for word, space in text:gmatch('(%S+)(%s*)') do
698 local temp = lines[#lines] .. word .. space:gsub('\n','')
699 if #temp > maxWidth then
700 table.insert(lines, '')
701 end
702 if space:find('\n') then
703 lines[#lines] = lines[#lines] .. word
704
705 space = space:gsub('\n', function()
706 table.insert(lines, '')
707 return ''
708 end)
709 else
710 lines[#lines] = lines[#lines] .. word .. space
711 end
712 end
713 if #lines[1] == 0 then
714 table.remove(lines,1)
715 end
716 return lines
717end
718
719TidyPath = function(path)
720 path = '/'..path
721 if fs.exists(path) and fs.isDir(path) or (OneOS and OneOS.FS.exists(path) and OneOS.FS.isDir(path)) then
722 path = path .. '/'
723 end
724
725 path, n = path:gsub("//", "/")
726 while n > 0 do
727 path, n = path:gsub("//", "/")
728 end
729 return path
730end
731
732Capitalise = function(str)
733 return str:sub(1, 1):upper() .. str:sub(2, -1)
734end
735
736Round = function(num, idp)
737 local mult = 10^(idp or 0)
738 return math.floor(num * mult + 0.5) / mult
739end
740]],
741["Object"] = [[
742X = 1
743Y = 1
744Width = 1
745Height = 1
746Parent = nil
747OnClick = nil
748Visible = true
749IgnoreClick = false
750Name = nil
751ClipDrawing = true
752UpdateDrawBlacklist = {}
753Fixed = false
754Ready = false
755
756DrawCache = {}
757
758NeedsDraw = function(self)
759 if not self.Visible then
760 return false
761 end
762
763 if not self.DrawCache.Buffer or self.DrawCache.AlwaysDraw or self.DrawCache.NeedsDraw then
764 return true
765 end
766
767 if self.OnNeedsUpdate then
768 if self.OnNeedsUpdate() then
769 return true
770 end
771 end
772
773 if self.Children then
774 for i, v in ipairs(self.Children) do
775 if v:NeedsDraw() then
776 return true
777 end
778 end
779 end
780end
781
782GetPosition = function(self)
783 return self.Bedrock:GetAbsolutePosition(self)
784end
785
786GetOffsetPosition = function(self)
787 if not self.Parent then
788 return {X = 1, Y = 1}
789 end
790
791 local offset = {X = 0, Y = 0}
792 if not self.Fixed and self.Parent.ChildOffset then
793 offset = self.Parent.ChildOffset
794 end
795
796 return {X = self.X + offset.X, Y = self.Y + offset.Y}
797end
798
799Draw = function(self)
800 if not self.Visible then
801 return
802 end
803
804 self.DrawCache.NeedsDraw = false
805 local pos = self:GetPosition()
806 Drawing.StartCopyBuffer()
807
808 if self.ClipDrawing then
809 Drawing.AddConstraint(pos.X, pos.Y, self.Width, self.Height)
810 end
811
812 if self.OnDraw then
813 self:OnDraw(pos.X, pos.Y)
814 end
815
816 self.DrawCache.Buffer = Drawing.EndCopyBuffer()
817
818 if self.Children then
819 for i, child in ipairs(self.Children) do
820 local pos = child:GetOffsetPosition()
821 if pos.Y + self.Height > 1 and pos.Y <= self.Height and pos.X + self.Width > 1 and pos.X <= self.Width then
822 child:Draw()
823 end
824 end
825 end
826
827
828 if self.OnPostChildrenDraw then
829 self:OnPostChildrenDraw(pos.X, pos.Y)
830 end
831
832 if self.ClipDrawing then
833 Drawing.RemoveConstraint()
834 end
835end
836
837ForceDraw = function(self, ignoreChildren, ignoreParent, ignoreBedrock)
838 if not ignoreBedrock and self.Bedrock then
839 self.Bedrock:ForceDraw()
840 end
841 self.DrawCache.NeedsDraw = true
842 if not ignoreParent and self.Parent then
843 self.Parent:ForceDraw(true, nil, true)
844 end
845 if not ignoreChildren and self.Children then
846 for i, child in ipairs(self.Children) do
847 child:ForceDraw(nil, true, true)
848 end
849 end
850end
851
852OnRemove = function(self)
853 if self == self.Bedrock:GetActiveObject() then
854 self.Bedrock:SetActiveObject()
855 end
856end
857
858local function ParseColour(value)
859 if type(value) == 'string' then
860 if colours[value] and type(colours[value]) == 'number' then
861 return colours[value]
862 elseif colors[value] and type(colors[value]) == 'number' then
863 return colors[value]
864 end
865 elseif type(value) == 'number' and (value == colours.transparent or (value >= colours.white and value <= colours.black)) then
866 return value
867 end
868 error('Invalid colour: "'..tostring(value)..'"')
869end
870
871Initialise = function(self, values)
872 local _new = values -- the new instance
873 _new.DrawCache = {
874 NeedsDraw = true,
875 AlwaysDraw = false,
876 Buffer = nil
877 }
878 setmetatable(_new, {__index = self} )
879
880 local new = {} -- the proxy
881 setmetatable(new, {
882 __index = function(t, k)
883 if k:find('Color') then
884 k = k:gsub('Color', 'Colour')
885 end
886
887 if k:find('Colour') and type(_new[k]) ~= 'table' and type(_new[k]) ~= 'function' then
888 if _new[k] then
889 return ParseColour(_new[k])
890 end
891 elseif _new[k] ~= nil then
892 return _new[k]
893 end
894 end,
895
896 __newindex = function (t,k,v)
897 if k:find('Color') then
898 k = k:gsub('Color', 'Colour')
899 end
900
901 if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then
902 v = new.Bedrock:ParseStringSize(new.Parent, k, v)
903 end
904
905 if v ~= _new[k] then
906 _new[k] = v
907 if t.OnUpdate then
908 t:OnUpdate(k)
909 end
910
911 if t.UpdateDrawBlacklist[k] == nil then
912 t:ForceDraw()
913 end
914 end
915 end
916 })
917 if new.OnInitialise then
918 new:OnInitialise()
919 end
920
921 return new
922end
923
924AnimateValue = function(self, valueName, from, to, duration, done, tbl)
925 tbl = tbl or self
926 if type(tbl[valueName]) ~= 'number' then
927 error('Animated value ('..valueName..') must be number.')
928 elseif not self.Bedrock.AnimationEnabled then
929 tbl[valueName] = to
930 if done then
931 done()
932 end
933 return
934 end
935 from = from or tbl[valueName]
936 duration = duration or 0.2
937 local delta = to - from
938
939 local startTime = os.clock()
940 local previousFrame = startTime
941 local frame
942 frame = function()
943 local time = os.clock()
944 local totalTime = time - startTime
945 local isLast = totalTime >= duration
946
947 if isLast then
948 tbl[valueName] = to
949 self:ForceDraw()
950 if done then
951 done()
952 end
953 else
954 tbl[valueName] = self.Bedrock.Helpers.Round(from + delta * (totalTime / duration))
955 self:ForceDraw()
956 self.Bedrock:StartTimer(function()
957 frame()
958 end, 0.05)
959 end
960 end
961 frame()
962end
963
964Click = function(self, event, side, x, y)
965 if self.Visible and not self.IgnoreClick then
966 if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then
967 return true
968 elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then
969 return true
970 elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then
971 return true
972 else
973 return false
974 end
975 else
976 return false
977 end
978
979end
980
981ToggleMenu = function(self, name, x, y)
982 return self.Bedrock:ToggleMenu(name, self, x, y)
983end
984
985function OnUpdate(self, value)
986 if value == 'Z' then
987 self.Bedrock:ReorderObjects()
988 end
989end
990]],
991["Peripheral"] = [[
992GetPeripheral = function(_type)
993 for i, p in ipairs(GetPeripherals()) do
994 if p.Type == _type then
995 return p
996 end
997 end
998end
999
1000Call = function(type, ...)
1001 local tArgs = {...}
1002 local p = GetPeripheral(type)
1003 peripheral.call(p.Side, unpack(tArgs))
1004end
1005
1006local getNames = peripheral.getNames or function()
1007 local tResults = {}
1008 for n,sSide in ipairs( rs.getSides() ) do
1009 if peripheral.isPresent( sSide ) then
1010 table.insert( tResults, sSide )
1011 local isWireless = false
1012 if pcall(function()isWireless = peripheral.call(sSide, 'isWireless') end) then
1013 isWireless = true
1014 end
1015 if peripheral.getType( sSide ) == "modem" and not isWireless then
1016 local tRemote = peripheral.call( sSide, "getNamesRemote" )
1017 for n,sName in ipairs( tRemote ) do
1018 table.insert( tResults, sName )
1019 end
1020 end
1021 end
1022 end
1023 return tResults
1024end
1025
1026GetPeripherals = function(filterType)
1027 local peripherals = {}
1028 for i, side in ipairs(getNames()) do
1029 local name = peripheral.getType(side):gsub("^%l", string.upper)
1030 local code = string.upper(side:sub(1,1))
1031 if side:find('_') then
1032 code = side:sub(side:find('_')+1)
1033 end
1034
1035 local dupe = false
1036 for i, v in ipairs(peripherals) do
1037 if v[1] == name .. ' ' .. code then
1038 dupe = true
1039 end
1040 end
1041
1042 if not dupe then
1043 local _type = peripheral.getType(side)
1044 local formattedType = _type:sub(1, 1):upper() .. _type:sub(2, -1)
1045 local isWireless = false
1046 if _type == 'modem' then
1047 if not pcall(function()isWireless = peripheral.call(side, 'isWireless') end) then
1048 isWireless = true
1049 end
1050 if isWireless then
1051 _type = 'wireless_modem'
1052 formattedType = 'Wireless Modem'
1053 name = 'W '..name
1054 end
1055 end
1056 if not filterType or _type == filterType then
1057 table.insert(peripherals, {Name = name:sub(1,8) .. ' '..code, Fullname = name .. ' ('..side:sub(1, 1):upper() .. side:sub(2, -1)..')', Side = side, Type = _type, Wireless = isWireless, FormattedType = formattedType})
1058 end
1059 end
1060 end
1061 return peripherals
1062end
1063
1064GetSide = function(side)
1065 for i, p in ipairs(GetPeripherals()) do
1066 if p.Side == side then
1067 return p
1068 end
1069 end
1070end
1071
1072PresentNamed = function(name)
1073 return peripheral.isPresent(name)
1074end
1075
1076CallType = function(type, ...)
1077 local tArgs = {...}
1078 local p = GetPeripheral(type)
1079 return peripheral.call(p.Side, unpack(tArgs))
1080end
1081
1082CallNamed = function(name, ...)
1083 local tArgs = {...}
1084 return peripheral.call(name, unpack(tArgs))
1085end
1086
1087GetInfo = function(p)
1088 local info = {}
1089 local buttons = {}
1090 if p.Type == 'computer' then
1091 local id = peripheral.call(p.Side:lower(),'getID')
1092 if id then
1093 info = {
1094 ID = tostring(id)
1095 }
1096 else
1097 info = {}
1098 end
1099 elseif p.Type == 'drive' then
1100 local discType = 'No Disc'
1101 local discID = nil
1102 local mountPath = nil
1103 local discLabel = nil
1104 local songName = nil
1105 if peripheral.call(p.Side:lower(), 'isDiskPresent') then
1106 if peripheral.call(p.Side:lower(), 'hasData') then
1107 discType = 'Data'
1108 discID = peripheral.call(p.Side:lower(), 'getDiskID')
1109 if discID then
1110 discID = tostring(discID)
1111 else
1112 discID = 'None'
1113 end
1114 mountPath = '/'..peripheral.call(p.Side:lower(), 'getMountPath')..'/'
1115 discLabel = peripheral.call(p.Side:lower(), 'getDiskLabel')
1116 else
1117 discType = 'Audio'
1118 songName = peripheral.call(p.Side:lower(), 'getAudioTitle')
1119 end
1120 end
1121 if mountPath then
1122 table.insert(buttons, {Text = 'View Files', OnClick = function(self, event, side, x, y)GoToPath(mountPath)end})
1123 elseif discType == 'Audio' then
1124 table.insert(buttons, {Text = 'Play', OnClick = function(self, event, side, x, y)
1125 if self.Text == 'Play' then
1126 disk.playAudio(p.Side:lower())
1127 self.Text = 'Stop'
1128 else
1129 disk.stopAudio(p.Side:lower())
1130 self.Text = 'Play'
1131 end
1132 end})
1133 else
1134 diskOpenButton = nil
1135 end
1136 if discType ~= 'No Disc' then
1137 table.insert(buttons, {Text = 'Eject', OnClick = function(self, event, side, x, y)disk.eject(p.Side:lower()) sleep(0) RefreshFiles() end})
1138 end
1139
1140 info = {
1141 ['Disc Type'] = discType,
1142 ['Disc Label'] = discLabel,
1143 ['Song Title'] = songName,
1144 ['Disc ID'] = discID,
1145 ['Mount Path'] = mountPath
1146 }
1147 elseif p.Type == 'printer' then
1148 local pageSize = 'No Loaded Page'
1149 local _, err = pcall(function() return tostring(peripheral.call(p.Side:lower(), 'getPgaeSize')) end)
1150 if not err then
1151 pageSize = tostring(peripheral.call(p.Side:lower(), 'getPageSize'))
1152 end
1153 info = {
1154 ['Paper Level'] = tostring(peripheral.call(p.Side:lower(), 'getPaperLevel')),
1155 ['Paper Size'] = pageSize,
1156 ['Ink Level'] = tostring(peripheral.call(p.Side:lower(), 'getInkLevel'))
1157 }
1158 elseif p.Type == 'modem' then
1159 info = {
1160 ['Connected Peripherals'] = tostring(#peripheral.call(p.Side:lower(), 'getNamesRemote'))
1161 }
1162 elseif p.Type == 'monitor' then
1163 local w, h = peripheral.call(p.Side:lower(), 'getSize')
1164 local screenType = 'Black and White'
1165 if peripheral.call(p.Side:lower(), 'isColour') then
1166 screenType = 'Colour'
1167 end
1168 local buttonTitle = 'Use as Screen'
1169 if OneOS.Settings:GetValues()['Monitor'] == p.Side:lower() then
1170 buttonTitle = 'Use Computer Screen'
1171 end
1172 table.insert(buttons, {Text = buttonTitle, OnClick = function(self, event, side, x, y)
1173 self.Bedrock:DisplayAlertWindow('Reboot Required', "To change screen you'll need to reboot your computer.", {'Reboot', 'Cancel'}, function(value)
1174 if value == 'Reboot' then
1175 if buttonTitle == 'Use Computer Screen' then
1176 OneOS.Settings:SetValue('Monitor', nil)
1177 else
1178 OneOS.Settings:SetValue('Monitor', p.Side:lower())
1179 end
1180 OneOS.Reboot()
1181 end
1182 end)
1183 end
1184 })
1185 info = {
1186 ['Type'] = screenType,
1187 ['Width'] = tostring(w),
1188 ['Height'] = tostring(h),
1189 }
1190 end
1191 info.Buttons = buttons
1192 return info
1193end
1194]],
1195}
1196local objects = {
1197["Button"] = [[
1198BackgroundColour = colours.lightGrey
1199ActiveBackgroundColour = colours.blue
1200ActiveTextColour = colours.white
1201TextColour = colours.black
1202DisabledTextColour = colours.lightGrey
1203Text = ""
1204Toggle = nil
1205Momentary = true
1206AutoWidth = true
1207Align = 'Center'
1208Enabled = true
1209
1210OnUpdate = function(self, value)
1211 if value == 'Text' and self.AutoWidth then
1212 self.Width = #self.Text + 2
1213 end
1214end
1215
1216OnDraw = function(self, x, y)
1217 local bg = self.BackgroundColour
1218
1219 if self.Toggle then
1220 bg = self.ActiveBackgroundColour
1221 end
1222
1223 local txt = self.TextColour
1224 if self.Toggle then
1225 txt = self.ActiveTextColour
1226 end
1227 if not self.Enabled then
1228 txt = self.DisabledTextColour
1229 end
1230 Drawing.DrawBlankArea(x, y, self.Width, self.Height, bg)
1231
1232 local _x = 1
1233 if self.Align == 'Right' then
1234 _x = self.Width - #self.Text - 1
1235 elseif self.Align == 'Center' then
1236 _x = math.floor((self.Width - #self.Text) / 2)
1237 end
1238
1239 Drawing.DrawCharacters(x + _x, y-1+math.ceil(self.Height/2), self.Text, txt, bg)
1240end
1241
1242OnLoad = function(self)
1243 if self.Toggle ~= nil then
1244 self.Momentary = false
1245 end
1246end
1247
1248Click = function(self, event, side, x, y)
1249 if self.Visible and not self.IgnoreClick and self.Enabled and event ~= 'mouse_scroll' then
1250 if self.OnClick then
1251 if self.Momentary then
1252 self.Toggle = true
1253 self.Bedrock:StartTimer(function()self.Toggle = false end,0.25)
1254 elseif self.Toggle ~= nil then
1255 self.Toggle = not self.Toggle
1256 end
1257
1258 self:OnClick(event, side, x, y, self.Toggle)
1259 else
1260 self.Toggle = not self.Toggle
1261 end
1262 return true
1263 else
1264 return false
1265 end
1266end
1267]],
1268["CollectionView"] = [[
1269Inherit = 'ScrollView'
1270UpdateDrawBlacklist = {['NeedsItemUpdate']=true}
1271
1272TextColour = colours.black
1273BackgroundColour = colours.white
1274Items = false
1275NeedsItemUpdate = false
1276SpacingX = 2
1277SpacingY = 1
1278
1279OnDraw = function(self, x, y)
1280 if self.NeedsItemUpdate then
1281 self:UpdateItems()
1282 self.NeedsItemUpdate = false
1283 end
1284 Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
1285end
1286
1287local function MaxIcons(self, obj)
1288 if not obj.Height or not obj.Width then
1289 error('You must provide each object\'s height when adding to a CollectionView.')
1290 end
1291 local slotHeight = obj.Height + self.SpacingY
1292 local slotWidth = obj.Width + self.SpacingX
1293 local maxX = math.floor((self.Width - 2) / slotWidth)
1294 return maxX, slotWidth, slotHeight
1295end
1296
1297local function IconLocation(self, obj, i)
1298 local maxX, slotWidth, slotHeight = MaxIcons(self, obj)
1299 local y = 2
1300 local x = 1 + math.ceil((self.Width - slotWidth * maxX) / 2)
1301 local rowPos = ((i - 1) % maxX)
1302 local colPos = math.ceil(i / maxX) - 1
1303 x = x + (slotWidth * rowPos)
1304 y = y + colPos * slotHeight
1305 return x, y
1306end
1307
1308local function AddItem(self, v, i)
1309 local toggle = false
1310 if not self.CanSelect then
1311 toggle = nil
1312 end
1313 local x, y = IconLocation(self, v, i)
1314 local item = {
1315 ["X"]=x,
1316 ["Y"]=y,
1317 ["Name"]="CollectionViewItem",
1318 ["Type"]="View",
1319 ["TextColour"]=self.TextColour,
1320 ["BackgroundColour"]=0,
1321 OnClick = function(itm)
1322 if self.CanSelect then
1323 for i2, _v in ipairs(self.Children) do
1324 _v.Toggle = false
1325 end
1326 self.Selected = itm
1327 end
1328 end
1329 }
1330 for k, _v in pairs(v) do
1331 item[k] = _v
1332 end
1333 self:AddObject(item)
1334end
1335
1336UpdateItems = function(self)
1337 self:RemoveAllObjects()
1338 local groupMode = false
1339 for k, v in pairs(self.Items) do
1340 if type(k) == 'string' then
1341 groupMode = true
1342 break
1343 end
1344 end
1345
1346 for i, v in ipairs(self.Items) do
1347 AddItem(self, v, i)
1348 end
1349 self:UpdateScroll()
1350end
1351
1352OnUpdate = function(self, value)
1353 if value == 'Items' then
1354 self.NeedsItemUpdate = true
1355 end
1356end
1357]],
1358["ImageView"] = [[
1359Image = false
1360
1361OnDraw = function(self, x, y)
1362 Drawing.DrawImage(x, y, self.Image, self.Width, self.Height)
1363end
1364
1365OnLoad = function(self)
1366 if self.Path and fs.exists(self.Path) then
1367 self.Image = Drawing.LoadImage(self.Path)
1368 end
1369end
1370
1371OnUpdate = function(self, value)
1372 if value == 'Path' then
1373 if self.Path and fs.exists(self.Path) then
1374 self.Image = Drawing.LoadImage(self.Path)
1375 end
1376 end
1377end
1378]],
1379["Label"] = [[
1380TextColour = colours.black
1381BackgroundColour = colours.transparent
1382Text = ""
1383AutoWidth = false
1384Wrap = true
1385Align = 'Left'
1386
1387local wrapText = function(text, maxWidth)
1388 local lines = {''}
1389 for word, space in text:gmatch('(%S+)(%s*)') do
1390 local temp = lines[#lines] .. word .. space:gsub('\n','')
1391 if #temp > maxWidth then
1392 table.insert(lines, '')
1393 end
1394 if space:find('\n') then
1395 lines[#lines] = lines[#lines] .. word
1396
1397 space = space:gsub('\n', function()
1398 table.insert(lines, '')
1399 return ''
1400 end)
1401 else
1402 lines[#lines] = lines[#lines] .. word .. space
1403 end
1404 end
1405 if #lines[1] == 0 then
1406 table.remove(lines,1)
1407 end
1408 return lines
1409end
1410
1411OnUpdate = function(self, value)
1412 if value == 'Text' then
1413 if self.AutoWidth then
1414 self.Width = #self.Text
1415 else
1416 self.Height = #wrapText(self.Text, self.Width)
1417 end
1418 end
1419end
1420
1421OnDraw = function(self, x, y)
1422 local lines
1423 if self.Wrap then
1424 lines = wrapText(self.Text, self.Width)
1425 else
1426 lines = {self.Bedrock.Helpers.TruncateString(self.Text, self.Width)}
1427 end
1428
1429 for i, v in ipairs(lines) do
1430 local _x = 0
1431 if self.Align == 'Right' then
1432 _x = self.Width - #v
1433 elseif self.Align == 'Center' then
1434 _x = math.floor((self.Width - #v) / 2)
1435 end
1436 Drawing.DrawCharacters(x + _x, y + i - 1, v, self.TextColour, self.BackgroundColour)
1437 end
1438end
1439]],
1440["ListView"] = [[
1441Inherit = 'ScrollView'
1442UpdateDrawBlacklist = {['NeedsItemUpdate']=true}
1443
1444TextColour = colours.black
1445BackgroundColour = colours.white
1446HeadingColour = colours.lightGrey
1447SelectionBackgroundColour = colours.blue
1448SelectionTextColour = colours.white
1449Items = false
1450CanSelect = false
1451Selected = nil
1452NeedsItemUpdate = false
1453ItemMargin = 1
1454HeadingMargin = 0
1455TopMargin = 0
1456
1457OnDraw = function(self, x, y)
1458 if self.NeedsItemUpdate then
1459 self:UpdateItems()
1460 end
1461 Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
1462end
1463
1464local function AddItem(self, v, x, y, group)
1465 local toggle = false
1466 if not self.CanSelect then
1467 toggle = nil
1468 elseif v.Selected then
1469 toggle = true
1470 end
1471 local item = {
1472 ["Width"]=self.Width,
1473 ["X"]=x,
1474 ["Y"]=y,
1475 ["Name"]="ListViewItem",
1476 ["Type"]="Button",
1477 ["TextColour"]=self.TextColour,
1478 ["BackgroundColour"]=0,
1479 ["ActiveTextColour"]=self.SelectionTextColour,
1480 ["ActiveBackgroundColour"]=self.SelectionBackgroundColour,
1481 ["Align"]='Left',
1482 ["Toggle"]=toggle,
1483 ["Group"]=group,
1484 OnClick = function(itm)
1485 if self.CanSelect then
1486 self:SelectItem(itm)
1487 elseif self.OnSelect then
1488 self:OnSelect(itm.Text)
1489 end
1490 end
1491 }
1492 if type(v) == 'table' then
1493 for k, _v in pairs(v) do
1494 item[k] = _v
1495 end
1496 else
1497 item.Text = v
1498 end
1499
1500 local itm = self:AddObject(item)
1501 if v.Selected then
1502 self:SelectItem(itm)
1503 end
1504end
1505
1506UpdateItems = function(self)
1507 if not self.Items or type(self.Items) ~= 'table' then
1508 self.Items = {}
1509 end
1510 self.Selected = nil
1511 self:RemoveAllObjects()
1512 local groupMode = false
1513 for k, v in pairs(self.Items) do
1514 if type(k) == 'string' then
1515 groupMode = true
1516 break
1517 end
1518 end
1519
1520 if not groupMode then
1521 for i, v in ipairs(self.Items) do
1522 AddItem(self, v, self.ItemMargin, i)
1523 end
1524 else
1525 local y = self.TopMargin
1526 for k, v in pairs(self.Items) do
1527 y = y + 1
1528 AddItem(self, {Text = k, TextColour = self.HeadingColour, IgnoreClick = true}, self.HeadingMargin, y)
1529 for i, _v in ipairs(v) do
1530 y = y + 1
1531 AddItem(self, _v, 1, y, k)
1532 end
1533 y = y + 1
1534 end
1535 end
1536 self:UpdateScroll()
1537 self.NeedsItemUpdate = false
1538end
1539
1540OnKeyChar = function(self, event, keychar)
1541 if keychar == keys.up or keychar == keys.down then
1542 local n = self:GetIndex(self.Selected)
1543 if keychar == keys.up then
1544 n = n - 1
1545 else
1546 n = n + 1
1547 end
1548 local new = self:GetNth(n)
1549 if new then
1550 self:SelectItem(new)
1551 end
1552 elseif keychar == keys.enter and self.Selected then
1553 self.Selected:Click('mouse_click', 1, 1, 1)
1554 end
1555end
1556
1557--returns the index/'n' of the given item
1558GetIndex = function(self, obj)
1559 local n = 1
1560 for i, v in ipairs(self.Children) do
1561 if not v.IgnoreClick then
1562 if obj == v then
1563 return n
1564 end
1565 n = n + 1
1566 end
1567 end
1568end
1569
1570--gets the 'nth' list item (does not include headings)
1571GetNth = function(self, n)
1572 local _n = 1
1573 for i, v in ipairs(self.Children) do
1574 if not v.IgnoreClick then
1575 if n == _n then
1576 return v
1577 end
1578 _n = _n + 1
1579 end
1580 end
1581end
1582
1583SelectItem = function(self, item)
1584 for i, v in ipairs(self.Children) do
1585 v.Toggle = false
1586 end
1587 self.Selected = item
1588 item.Toggle = true
1589 if self.OnSelect then
1590 self:OnSelect(item.Text)
1591 end
1592end
1593
1594OnUpdate = function(self, value)
1595 if value == 'Items' then
1596 self.NeedsItemUpdate = true
1597 end
1598end
1599]],
1600["Menu"] = [[
1601Inherit = 'View'
1602
1603TextColour = colours.black
1604BackgroundColour = colours.white
1605HideTop = false
1606Prepared = false
1607
1608OnDraw = function(self, x, y)
1609 Drawing.IgnoreConstraint = true
1610 Drawing.DrawBlankArea(x + 1, y + (self.HideTop and 0 or 1), self.Width, self.Height + (self.HideTop and 1 or 0), colours.grey)
1611 Drawing.IgnoreConstraint = false
1612 Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
1613end
1614
1615OnLoad = function(self)
1616 local owner = self.Owner
1617 if type(owner) == 'string' then
1618 owner = self.Bedrock:GetObject(self.Owner)
1619 end
1620
1621 if owner then
1622 if self.X == 0 and self.Y == 0 then
1623 local pos = owner:GetPosition()
1624 self.X = pos.X
1625 self.Y = pos.Y + owner.Height
1626 end
1627 self.Owner = owner
1628 else
1629 self.Owner = nil
1630 end
1631end
1632
1633OnUpdate = function(self, value)
1634 if value == 'Children' then
1635 self.Height = #self.Children + 1 + (self.HideTop and 0 or 1)
1636 if not self.BaseY then
1637 self.BaseY = self.Y
1638 end
1639 if #self.Children > 0 and self.Children[1].Type == 'Button' then
1640 self.Width = self.Bedrock.Helpers.LongestString(self.Children, 'Text') + 2
1641 for i, v in ipairs(self.Children) do
1642 if v.TextColour then
1643 v.TextColour = self.TextColour
1644 end
1645 if v.BackgroundColour then
1646 v.BackgroundColour = colours.transparent
1647 end
1648 if v.Colour then
1649 v.Colour = colours.lightGrey
1650 end
1651 v.Align = 'Left'
1652 v.X = 1
1653 v.Y = i + (self.HideTop and 0 or 1)
1654 v.Width = self.Width
1655 v.Height = 1
1656 end
1657 elseif #self.Children > 0 and self.Children[1].Type == 'MenuItem' then
1658 local width = 1
1659 for i, v in ipairs(self.Children) do
1660 if v.Width > width then
1661 width = v.Width
1662 end
1663 end
1664 self.Width = width
1665 for i, v in ipairs(self.Children) do
1666 if v.TextColour then
1667 v.TextColour = self.TextColour
1668 end
1669 if v.Colour then
1670 v.Colour = colours.lightGrey
1671 end
1672 v.X = 1
1673 v.Y = i + (self.HideTop and 0 or 1)
1674 v.Width = width
1675 v.Height = 1
1676 end
1677 end
1678
1679 self.Y = self.BaseY
1680 local pos = self:GetPosition()
1681 if pos.Y + self.Height + 1 > Drawing.Screen.Height then
1682 self.Y = self.BaseY - ((self.Height + pos.Y) - Drawing.Screen.Height)
1683 end
1684
1685 if pos.X + self.Width > Drawing.Screen.Width then
1686 self.X = Drawing.Screen.Width - self.Width
1687 end
1688 end
1689end
1690
1691Close = function(self, isBedrockCall)
1692 self.Bedrock.Menu = nil
1693 if not self.Prepared then
1694 self.Parent:RemoveObject(self)
1695 else
1696 self.Visible = false
1697 end
1698
1699 if self.Owner and self.Owner.Toggle then
1700 self.Owner.Toggle = false
1701 end
1702 self.Parent:ForceDraw()
1703 self = nil
1704end
1705
1706OnChildClick = function(self, child, event, side, x, y)
1707 self:Close()
1708end
1709]],
1710["MenuItem"] = [[
1711BackgroundColour = colours.white
1712TextColour = colours.black
1713DisabledTextColour = colours.lightGrey
1714ShortcutTextColour = colours.grey
1715Text = ""
1716Enabled = true
1717Shortcut = nil
1718ShortcutPadding = 2
1719ShortcutName = nil
1720
1721OnUpdate = function(self, value)
1722 if value == 'Text' then
1723 if self.Shortcut then
1724 self.Width = #self.Text + 2 + self.ShortcutPadding + #self.Shortcut
1725 else
1726 self.Width = #self.Text + 2
1727 end
1728 elseif value == 'OnClick' then
1729 self:RegisterShortcut()
1730 end
1731end
1732
1733OnDraw = function(self, x, y)
1734 Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
1735
1736 local txt = self.TextColour
1737 if not self.Enabled then
1738 txt = self.DisabledTextColour
1739 end
1740 Drawing.DrawCharacters(x + 1, y, self.Text, txt, colours.transparent)
1741
1742 if self.Shortcut then
1743 local shrt = self.ShortcutTextColour
1744 if not self.Enabled then
1745 shrt = self.DisabledTextColour
1746 end
1747 Drawing.DrawCharacters(x + self.Width - #self.Shortcut - 1, y, self.Shortcut, shrt, colours.transparent)
1748 end
1749end
1750
1751ParseShortcut = function(self)
1752 local special = {
1753 ['^'] = keys.leftShift,
1754 ['<'] = keys.delete,
1755 ['>'] = keys.delete,
1756 ['#'] = keys.leftCtrl,
1757 ['~'] = keys.leftAlt,
1758 }
1759
1760 local keys = {}
1761 for i = 1, #self.Shortcut do
1762 local c = self.Shortcut:sub(i,i)
1763 table.insert(keys, special[c] or c:lower())
1764 end
1765 return keys
1766end
1767
1768RegisterShortcut = function(self)
1769 if self.Shortcut then
1770 self.Shortcut = self.Shortcut:upper()
1771 self.ShortcutName = self.Bedrock:RegisterKeyboardShortcut(self:ParseShortcut(), function()
1772 if self.OnClick and self.Enabled then
1773 if self.Parent.Owner then
1774 self.Parent:Close()
1775 self.Parent.Owner.Toggle = true
1776 self.Bedrock:StartTimer(function()
1777 self.Parent.Owner.Toggle = false
1778 end, 0.3)
1779 end
1780 return self:OnClick('keyboard_shortcut', 1, 1, 1)
1781 else
1782 return false
1783 end
1784 end)
1785 end
1786end
1787
1788OnRemove = function(self)
1789 if self.ShortcutName then
1790 self.Bedrock:UnregisterKeyboardShortcut(self.ShortcutName)
1791 end
1792end
1793
1794OnLoad = function(self)
1795 if self.OnClick ~= nil then
1796 self:RegisterShortcut()
1797 end
1798 -- self:OnUpdate('Text')
1799end
1800]],
1801["NumberBox"] = [[
1802Inherit = 'View'
1803
1804Value = 1
1805Minimum = 1
1806Maximum = 99
1807BackgroundColour = colours.lightGrey
1808TextBoxTimer = nil
1809Width = 7
1810
1811OnLoad = function(self)
1812 self:AddObject({
1813 X = self.Width - 1,
1814 Y = 1,
1815 Width = 1,
1816 AutoWidth = false,
1817 Text = '-',
1818 Type = 'Button',
1819 Name = 'AddButton',
1820 BackgroundColour = colours.transparent,
1821 OnClick = function()
1822 self:ShiftValue(-1)
1823 end
1824 })
1825
1826 self:AddObject({
1827 X = self.Width,
1828 Y = 1,
1829 Width = 1,
1830 AutoWidth = false,
1831 Text = '+',
1832 Type = 'Button',
1833 Name = 'SubButton',
1834 BackgroundColour = colours.transparent,
1835 OnClick = function()
1836 self:ShiftValue(1)
1837 end
1838 })
1839
1840 self:AddObject({
1841 X = 1,
1842 Y = 1,
1843 Width = self.Width - 2,
1844 Text = tostring(self.Value),
1845 Align = 'Center',
1846 Type = 'TextBox',
1847 BackgroundColour = colours.transparent,
1848 OnChange = function(_self, event, keychar)
1849 if keychar == keys.enter then
1850 self:SetValue(tonumber(_self.Text))
1851 self.TextBoxTimer = nil
1852 end
1853 if self.TextBoxTimer then
1854 self.Bedrock:StopTimer(self.TextBoxTimer)
1855 end
1856
1857 self.TextBoxTimer = self.Bedrock:StartTimer(function(_, timer)
1858 if timer and timer == self.TextBoxTimer then
1859 self:SetValue(tonumber(_self.Text))
1860 self.TextBoxTimer = nil
1861 end
1862 end, 2)
1863 end
1864 })
1865end
1866
1867OnScroll = function(self, event, dir, x, y)
1868 self:ShiftValue(-dir)
1869end
1870
1871ShiftValue = function(self, delta)
1872 local val = tonumber(self:GetObject('TextBox').Text) or self.Minimum
1873 self:SetValue(val + delta)
1874end
1875
1876SetValue = function(self, newValue)
1877 newValue = newValue or 0
1878 if self.Maximum and newValue > self.Maximum then
1879 newValue = self.Maximum
1880 elseif self.Minimum and newValue < self.Minimum then
1881 newValue = self.Minimum
1882 end
1883 self.Value = newValue
1884 if self.OnChange then
1885 self:OnChange()
1886 end
1887end
1888
1889OnUpdate = function(self, value)
1890 if value == 'Value' then
1891 local textbox = self:GetObject('TextBox')
1892 if textbox then
1893 textbox.Text = tostring(self.Value)
1894 end
1895 end
1896end
1897]],
1898["ProgressBar"] = [[
1899BackgroundColour = colours.lightGrey
1900BarColour = colours.blue
1901TextColour = colours.white
1902ShowText = false
1903Value = 0
1904Maximum = 1
1905Indeterminate = false
1906AnimationStep = 0
1907
1908OnDraw = function(self, x, y)
1909 Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
1910
1911 -- if self.Indeterminate then
1912 -- for i = 1, self.Width do
1913 -- local s = x + i - 1 + self.AnimationStep
1914 -- if s % 4 == 1 or s % 4 == 2 then
1915 -- Drawing.DrawBlankArea(s, y, 1, self.Height, self.BarColour)
1916 -- end
1917 -- end
1918 -- self.AnimationStep = self.AnimationStep + 1
1919 -- if self.AnimationStep >= 4 then
1920 -- self.AnimationStep = 0
1921 -- end
1922 -- self.Bedrock:StartTimer(function()
1923 -- self:Draw()
1924 -- end, 0.25)
1925 -- else
1926 local values = self.Value
1927 local barColours = self.BarColour
1928 if type(values) == 'number' then
1929 values = {values}
1930 end
1931 if type(barColours) == 'number' then
1932 barColours = {barColours}
1933 end
1934
1935 local total = 0
1936 local _x = x
1937 for i, v in ipairs(values) do
1938 local width = (v == 0 and 0 or self.Bedrock.Helpers.Round((v / self.Maximum) * self.Width))
1939 total = total + v
1940 if width ~= 0 then
1941 Drawing.DrawBlankArea(_x, y, width, self.Height, barColours[((i-1)%#barColours)+1])
1942 end
1943 _x = _x + width
1944 end
1945
1946 if self.ShowText then
1947 local text = self.Bedrock.Helpers.Round((total / self.Maximum) * 100) .. '%'
1948 Drawing.DrawCharactersCenter(x, y, self.Width, self.Height, text, self.TextColour, colours.transparent)
1949 end
1950 -- end
1951end
1952]],
1953["RadioButton"] = [[
1954BackgroundColour = colours.white
1955ActiveBackgroundColour = colours.green
1956TextColour = colours.black
1957SelectedText = "#"
1958UnselectedText = "o"
1959Text = ""
1960Friends = ""
1961AutoWidth = true
1962Selected = false
1963Enabled = true
1964
1965OnUpdate = function(self, value)
1966 if value == "Text" and self.AutoWidth then
1967 self.Width = #self.Text + 2
1968 end
1969
1970 if value == "Selected" and self.Selected and self.Bedrock and self.Bedrock.View then
1971 if type(self.Friends) == "string" then
1972 local friend = self.Bedrock:GetObject(self.Friends)
1973
1974 if friend and friend.Type == "RadioButton" and friend.Enabled then
1975 friend.Selected = false
1976 end
1977 elseif type(self.Friends) == "table" then
1978 for _,v in ipairs(self.Friends) do
1979 local friend = self.Bedrock:GetObject(v)
1980
1981 if friend and friend.Type == "RadioButton" and friend.Enabled then
1982 friend.Selected = false
1983 end
1984 end
1985 end
1986 end
1987end
1988
1989OnLoad = function(self)
1990 if self.AutoWidth then
1991 self.Width = #self.Text + 2
1992 end
1993
1994 self.Height = 1
1995end
1996
1997OnDraw = function(self, x, y)
1998 Drawing.DrawCharacters(x, y, self.Selected and self.SelectedText or self.UnselectedText, self.Selected and colours.green or colours.lightGrey, colours.white)
1999 Drawing.DrawCharacters(x + 2, y, self.Text, self.TextColour, colours.transparent)
2000end
2001
2002Click = function(self, event, side, x, y)
2003 if self.Visible and not self.IgnoreClick and self.Enabled and event ~= "mouse_scroll" then
2004 self.Selected = true
2005
2006 if self.OnSelectionChange then
2007 self:OnSelectionChange()
2008 end
2009
2010 return true
2011 end
2012end
2013]],
2014["ScrollBar"] = [[
2015BackgroundColour = colours.lightGrey
2016BarColour = colours.lightBlue
2017Scroll = 0
2018MaxScroll = 0
2019ClickPoint = nil
2020Fixed = true
2021
2022OnUpdate = function(self, value)
2023 if value == 'Text' and self.AutoWidth then
2024 self.Width = #self.Text + 2
2025 end
2026end
2027
2028OnDraw = function(self, x, y)
2029 local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))
2030 if barHeight < 3 then
2031 barHeight = 3
2032 end
2033 local percentage = (self.Scroll/self.MaxScroll)
2034
2035 Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
2036 Drawing.DrawBlankArea(x, y + math.ceil(self.Height*percentage - barHeight*percentage), self.Width, barHeight, self.BarColour)
2037end
2038
2039OnScroll = function(self, event, direction, x, y)
2040 if event == 'mouse_scroll' then
2041 direction = self.Bedrock.Helpers.Round(direction * 3)
2042 end
2043 if self.Scroll < 0 or self.Scroll > self.MaxScroll then
2044 return false
2045 end
2046 local old = self.Scroll
2047 self.Scroll = self.Bedrock.Helpers.Round(self.Scroll + direction)
2048 if self.Scroll < 0 then
2049 self.Scroll = 0
2050 elseif self.Scroll > self.MaxScroll then
2051 self.Scroll = self.MaxScroll
2052 end
2053
2054 if self.Scroll ~= old and self.OnChange then
2055 self:OnChange()
2056 end
2057end
2058
2059-- TODO: this really needs an overhaul
2060OnClick = function(self, event, side, x, y)
2061 if event == 'mouse_click' then
2062 self.ClickPoint = y
2063 else
2064 if self.ClickPoint then
2065 local gapHeight = self.Height - (self.Height * (self.Height / (self.Height + self.MaxScroll)))
2066 local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))
2067 --local delta = (self.Height + self.MaxScroll) * ((y - self.ClickPoint) / barHeight)
2068 local delta = ((y - self.ClickPoint)/gapHeight)*self.MaxScroll
2069 --l(((y - self.ClickPoint)/gapHeight))
2070 --l(delta)
2071 self.Scroll = self.Bedrock.Helpers.Round(delta)
2072 --l(self.Scroll)
2073 --l('----')
2074 if self.Scroll < 0 then
2075 self.Scroll = 0
2076 elseif self.Scroll > self.MaxScroll then
2077 self.Scroll = self.MaxScroll
2078 end
2079 if self.OnChange then
2080 self:OnChange()
2081 end
2082 end
2083 end
2084
2085 local relScroll = self.MaxScroll * ((y-1)/self.Height)
2086 if y == self.Height then
2087 relScroll = self.MaxScroll
2088 end
2089 self.Scroll = self.Bedrock.Helpers.Round(relScroll)
2090
2091
2092end
2093
2094OnDrag = OnClick
2095]],
2096["ScrollView"] = [[
2097Inherit = 'View'
2098ChildOffset = false
2099ContentWidth = 0
2100ContentHeight = 0
2101ScrollBarBackgroundColour = colours.lightGrey
2102ScrollBarColour = colours.lightBlue
2103
2104CalculateContentSize = function(self)
2105 local function calculateObject(obj)
2106 local pos = obj:GetPosition()
2107 local x2 = pos.X + obj.Width - 1
2108 local y2 = pos.Y + obj.Height - 1
2109 if obj.Children then
2110 for i, child in ipairs(obj.Children) do
2111 local _x2, _y2 = calculateObject(child)
2112 if _x2 > x2 then
2113 x2 = _x2
2114 end
2115 if _y2 > y2 then
2116 y2 = _y2
2117 end
2118 end
2119 end
2120 return x2, y2
2121 end
2122
2123 local pos = self:GetPosition()
2124 local x2, y2 = calculateObject(self)
2125 self.ContentWidth = x2 - pos.X + 1
2126 self.ContentHeight = y2 - pos.Y + 1
2127end
2128
2129UpdateScroll = function(self)
2130 self.ChildOffset.Y = 0
2131 self:CalculateContentSize()
2132 if self.ContentHeight > self.Height then
2133 if not self:GetObject('ScrollViewScrollBar') then
2134 local _scrollBar = self:AddObject({
2135 ["Name"] = 'ScrollViewScrollBar',
2136 ["Type"] = 'ScrollBar',
2137 ["X"] = self.Width,
2138 ["Y"] = 1,
2139 ["Width"] = 1,
2140 ["Height"] = self.Height,
2141 ["BackgroundColour"] = self.ScrollBarBackgroundColour,
2142 ["BarColour"] = self.ScrollBarColour,
2143 ["Z"]=999
2144 })
2145
2146 _scrollBar.OnChange = function(scrollBar)
2147 self.ChildOffset.Y = -scrollBar.Scroll
2148 for i, child in ipairs(self.Children) do
2149 child:ForceDraw()
2150 end
2151 end
2152 end
2153
2154 if self:GetObject('ScrollViewScrollBar') then
2155 self:GetObject('ScrollViewScrollBar').MaxScroll = self.ContentHeight - self.Height
2156 end
2157 else
2158 self:RemoveObject('ScrollViewScrollBar')
2159 end
2160end
2161
2162OnScroll = function(self, event, direction, x, y)
2163 if self:GetObject('ScrollViewScrollBar') then
2164 self:GetObject('ScrollViewScrollBar'):OnScroll(event, direction, x, y)
2165 end
2166end
2167
2168OnLoad = function(self)
2169 if not self.ChildOffset or not self.ChildOffset.X or not self.ChildOffset.Y then
2170 self.ChildOffset = {X = 0, Y = 0}
2171 end
2172 self:UpdateScroll()
2173end
2174]],
2175["SecureTextBox"] = [[
2176Inherit = 'TextBox'
2177MaskCharacter = '*'
2178
2179OnDraw = function(self, x, y)
2180 Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
2181 if self.CursorPos > #self.Text then
2182 self.CursorPos = #self.Text
2183 elseif self.CursorPos < 0 then
2184 self.CursorPos = 0
2185 end
2186 local text = ''
2187
2188 for i = 1, #self.Text do
2189 text = text .. self.MaskCharacter
2190 end
2191
2192 if self.Bedrock:GetActiveObject() == self then
2193 if #text > (self.Width - 2) then
2194 text = text:sub(#text-(self.Width - 3))
2195 self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}
2196 else
2197 self.Bedrock.CursorPos = {x + 1 + self.CursorPos, y}
2198 end
2199 self.Bedrock.CursorColour = self.TextColour
2200 end
2201
2202 if #tostring(text) == 0 then
2203 Drawing.DrawCharacters(x + 1, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)
2204 else
2205 if not self.Selected then
2206 Drawing.DrawCharacters(x + 1, y, text, self.TextColour, self.BackgroundColour)
2207 else
2208 for i = 1, #text do
2209 local char = text:sub(i, i)
2210 local textColour = self.TextColour
2211 local backgroundColour = self.BackgroundColour
2212 if i > self.DragStart and i - 1 <= self.CursorPos then
2213 textColour = self.SelectedTextColour
2214 backgroundColour = self.SelectedBackgroundColour
2215 end
2216 Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)
2217 end
2218 end
2219 end
2220end
2221]],
2222["Separator"] = [[
2223Colour = colours.grey
2224Character = nil
2225
2226OnDraw = function(self, x, y)
2227 local char = self.Character
2228 if not char then
2229 char = "|"
2230 if self.Width > self.Height then
2231 char = '-'
2232 end
2233 end
2234 Drawing.DrawArea(x, y, self.Width, self.Height, char, self.Colour, colours.transparent)
2235end
2236]],
2237["TextBox"] = [[
2238BackgroundColour = colours.lightGrey
2239SelectedBackgroundColour = colours.blue
2240SelectedTextColour = colours.white
2241TextColour = colours.black
2242PlaceholderTextColour = colours.grey
2243Placeholder = ''
2244AutoWidth = false
2245Text = ""
2246CursorPos = nil
2247Numerical = false
2248DragStart = nil
2249Selected = false
2250SelectOnClick = false
2251ActualDragStart = nil
2252
2253OnDraw = function(self, x, y)
2254 Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
2255 if self.CursorPos > #self.Text then
2256 self.CursorPos = #self.Text
2257 elseif self.CursorPos < 0 then
2258 self.CursorPos = 0
2259 end
2260 local text = self.Text
2261 local offset = self:TextOffset()
2262 if #text > (self.Width - 2) then
2263 text = text:sub(offset+1, offset + self.Width - 2)
2264 -- self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}
2265 -- else
2266 end
2267 if self.Bedrock:GetActiveObject() == self then
2268 self.Bedrock.CursorPos = {x + 1 + self.CursorPos - offset, y}
2269 self.Bedrock.CursorColour = self.TextColour
2270 else
2271 self.Selected = false
2272 end
2273
2274 if #tostring(text) == 0 then
2275 self:DrawPlaceholder(x + 1, y)
2276 else
2277 if not self.Selected then
2278 self:DrawText(x + 1, y, text)
2279 else
2280 local startPos = self.DragStart - offset
2281 local endPos = self.CursorPos - offset
2282 if startPos > endPos then
2283 startPos = self.CursorPos - offset
2284 endPos = self.DragStart - offset
2285 end
2286 self:DrawSelectedText(x + 1, y, text, startPos, endPos)
2287 end
2288 end
2289end
2290
2291DrawText = function(self, x, y, text)
2292 Drawing.DrawCharacters(x, y, text, self.TextColour, self.BackgroundColour)
2293end
2294
2295DrawPlaceholder = function(self, x, y)
2296 Drawing.DrawCharacters(x, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)
2297end
2298
2299DrawSelectedText = function(self, x, y, text, startPos, endPos)
2300 for i = 1, #text do
2301 local char = text:sub(i, i)
2302 local textColour = self.TextColour
2303 local backgroundColour = self.BackgroundColour
2304
2305 if i > startPos and i - 1 <= endPos then
2306 textColour = self.SelectedTextColour
2307 backgroundColour = self.SelectedBackgroundColour
2308 end
2309 Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)
2310 end
2311end
2312
2313TextOffset = function(self)
2314 if #self.Text < (self.Width - 2) then
2315 return 0
2316 elseif self.Bedrock:GetActiveObject() ~= self then
2317 return 0
2318 else
2319 local textWidth = (self.Width - 2)
2320 local offset = self.CursorPos - textWidth
2321 if offset < 0 then
2322 offset = 0
2323 end
2324 return offset
2325 end
2326end
2327
2328OnLoad = function(self)
2329 if not self.CursorPos then
2330 self.CursorPos = #self.Text
2331 end
2332end
2333
2334OnClick = function(self, event, side, x, y)
2335 if self.Bedrock:GetActiveObject() ~= self and self.SelectOnClick then
2336 self.CursorPos = #self.Text - 1
2337 self.DragStart = 0
2338 self.ActualDragStart = x - 2 + self:TextOffset()
2339 self.Selected = true
2340 else
2341 self.CursorPos = x - 2 + self:TextOffset()
2342 self.DragStart = self.CursorPos
2343 self.Selected = false
2344 end
2345 self.Bedrock:SetActiveObject(self)
2346end
2347
2348OnDrag = function(self, event, side, x, y)
2349 self.CursorPos = x - 2 + self:TextOffset()
2350 if self.ActualDragStart then
2351 self.DragStart = self.ActualDragStart
2352 self.ActualDragStart = nil
2353 end
2354 if self.DragStart then
2355 self.Selected = true
2356 end
2357end
2358
2359OnKeyChar = function(self, event, keychar)
2360 local deleteSelected = function()
2361 if self.Selected then
2362 local startPos = self.DragStart
2363 local endPos = self.CursorPos
2364 if startPos > endPos then
2365 startPos = self.CursorPos
2366 endPos = self.DragStart
2367 end
2368 self.Text = self.Text:sub(1, startPos) .. self.Text:sub(endPos + 2)
2369 self.CursorPos = startPos
2370 self.DragStart = nil
2371 self.Selected = false
2372 return true
2373 end
2374 end
2375
2376 if self.CustomOnKeyChar and self:CustomOnKeyChar(event, keychar) then
2377 self:OnChange(event, keychar)
2378 return
2379 elseif event == 'char' then
2380 deleteSelected()
2381 if self.Numerical then
2382 keychar = tostring(tonumber(keychar))
2383 end
2384 if keychar == 'nil' then
2385 return
2386 end
2387 self.Text = string.sub(self.Text, 1, self.CursorPos ) .. keychar .. string.sub( self.Text, self.CursorPos + 1 )
2388 if self.Numerical then
2389 self.Text = tostring(tonumber(self.Text))
2390 if self.Text == 'nil' then
2391 self.Text = '1'
2392 end
2393 end
2394
2395 self.CursorPos = self.CursorPos + 1
2396 if self.OnChange then
2397 self:OnChange(event, keychar)
2398 end
2399 return false
2400 elseif event == 'key' then
2401 if keychar == keys.enter then
2402 if self.OnChange then
2403 self:OnChange(event, keychar)
2404 end
2405 elseif keychar == keys.left then
2406 -- Left
2407 if self.CursorPos > 0 then
2408 if self.Selected then
2409 self.CursorPos = self.DragStart
2410 self.DragStart = nil
2411 self.Selected = false
2412 else
2413 self.CursorPos = self.CursorPos - 1
2414 end
2415 if self.OnChange then
2416 self:OnChange(event, keychar)
2417 end
2418 end
2419
2420 elseif keychar == keys.right then
2421 -- Right
2422 if self.CursorPos < string.len(self.Text) then
2423 if self.Selected then
2424 self.CursorPos = self.CursorPos
2425 self.DragStart = nil
2426 self.Selected = false
2427 else
2428 self.CursorPos = self.CursorPos + 1
2429 end
2430 if self.OnChange then
2431 self:OnChange(event, keychar)
2432 end
2433 end
2434
2435 elseif keychar == keys.backspace then
2436 -- Backspace
2437 if not deleteSelected() and self.CursorPos > 0 then
2438 self.Text = string.sub( self.Text, 1, self.CursorPos - 1 ) .. string.sub( self.Text, self.CursorPos + 1 )
2439 self.CursorPos = self.CursorPos - 1
2440 if self.Numerical then
2441 self.Text = tostring(tonumber(self.Text))
2442 if self.Text == 'nil' then
2443 self.Text = '1'
2444 end
2445 end
2446 if self.OnChange then
2447 self:OnChange(event, keychar)
2448 end
2449 end
2450 elseif keychar == keys.home then
2451 -- Home
2452 self.CursorPos = 0
2453 if self.OnChange then
2454 self:OnChange(event, keychar)
2455 end
2456 elseif keychar == keys.delete then
2457 if not deleteSelected() and self.CursorPos < string.len(self.Text) then
2458 self.Text = string.sub( self.Text, 1, self.CursorPos ) .. string.sub( self.Text, self.CursorPos + 2 )
2459 if self.Numerical then
2460 self.Text = tostring(tonumber(self.Text))
2461 if self.Text == 'nil' then
2462 self.Text = '1'
2463 end
2464 end
2465 if self.OnChange then
2466 self:OnChange(keychar)
2467 end
2468 end
2469 elseif keychar == keys["end"] then
2470 -- End
2471 self.CursorPos = string.len(self.Text)
2472 else
2473 if self.OnChange then
2474 self:OnChange(event, keychar)
2475 end
2476 return false
2477 end
2478 end
2479end
2480]],
2481["View"] = [[
2482BackgroundColour = colours.transparent
2483Children = {}
2484
2485OnDraw = function(self, x, y)
2486 if self.BackgroundColour then
2487 Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
2488 end
2489end
2490
2491OnInitialise = function(self)
2492 self.Children = {}
2493end
2494
2495InitialiseFile = function(self, bedrock, file, name)
2496 local _new = {}
2497 _new.X = 1
2498 _new.Y = 1
2499 _new.Width = Drawing.Screen.Width
2500 _new.Height = Drawing.Screen.Height
2501 _new.BackgroundColour = file.BackgroundColour
2502 _new.Name = name
2503 _new.Children = {}
2504 _new.Bedrock = bedrock
2505 local new = self:Initialise(_new)
2506 for i, obj in ipairs(file.Children) do
2507 local view = bedrock:ObjectFromFile(obj, new)
2508 if not view.Z then
2509 view.Z = i
2510 end
2511 view.Parent = new
2512 table.insert(new.Children, view)
2513 end
2514 return new
2515end
2516
2517function CheckClick(self, object, x, y)
2518 local offset = {X = 0, Y = 0}
2519 if not object.Fixed and self.ChildOffset then
2520 offset = self.ChildOffset
2521 end
2522 if object.X + offset.X <= x and object.Y + offset.Y <= y and object.X + offset.X + object.Width > x and object.Y + offset.Y + object.Height > y then
2523 return true
2524 end
2525end
2526
2527function DoClick(self, object, event, side, x, y)
2528 if object then
2529 if self:CheckClick(object, x, y) then
2530 local offset = {X = 0, Y = 0}
2531 if not object.Fixed and self.ChildOffset then
2532 offset = self.ChildOffset
2533 end
2534 return object:Click(event, side, x - object.X - offset.X + 1, y - object.Y + 1 - offset.Y)
2535 end
2536 end
2537end
2538
2539Click = function(self, event, side, x, y, z)
2540 if self.Visible and not self.IgnoreClick then
2541 for i = #self.Children, 1, -1 do --children are ordered from smallest Z to highest, so this is done in reverse
2542 local child = self.Children[i]
2543 if self:DoClick(child, event, side, x, y) then
2544 if self.OnChildClick then
2545 self:OnChildClick(child, event, side, x, y)
2546 end
2547 return true
2548 end
2549 end
2550 if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then
2551 return true
2552 elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then
2553 return true
2554 elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then
2555 return true
2556 else
2557 return false
2558 end
2559 else
2560 return false
2561 end
2562end
2563
2564OnRemove = function(self)
2565 if self == self.Bedrock:GetActiveObject() then
2566 self.Bedrock:SetActiveObject()
2567 end
2568 for i, child in ipairs(self.Children) do
2569 child:OnRemove()
2570 end
2571end
2572
2573local function findObjectNamed(view, name, minI)
2574 local minI = minI or 0
2575 if view and view.Children then
2576 for i, child in ipairs(view.Children) do
2577 if child.Name == name or child == name then
2578 return child, i, view
2579 elseif child.Children then
2580 local found, index, foundView = findObjectNamed(child, name)
2581 if found and minI <= index then
2582 return found, index, foundView
2583 end
2584 end
2585 end
2586 end
2587end
2588
2589function ReorderObjects(self)
2590 if self.Children then
2591 table.sort(self.Children, function(a,b)
2592 return a.Z < b.Z
2593 end)
2594 for i, v in ipairs(self.Children) do
2595 if v.ReorderObjects then
2596 v:ReorderObjects()
2597 end
2598 end
2599 end
2600end
2601
2602function AddObject(self, info, extra, first)
2603 if type(info) == 'string' then
2604 local h = fs.open(self.Bedrock.ViewPath..info..'.view', 'r')
2605 if h then
2606 info = textutils.unserialize(h.readAll())
2607 h.close()
2608 else
2609 error('Error in opening object: '..info)
2610 end
2611 end
2612
2613 if extra then
2614 for k, v in pairs(extra) do
2615 if v then
2616 info[k] = v
2617 end
2618 end
2619 end
2620
2621 local view = self.Bedrock:ObjectFromFile(info, self)
2622 if not view.Z then
2623 if first then
2624 view.Z = 1
2625 else
2626 view.Z = #self.Children + 1
2627 end
2628 end
2629
2630 if first then
2631 table.insert(self.Children, 1, view)
2632 else
2633 table.insert(self.Children, view)
2634 end
2635 if self.Bedrock.View then
2636 self.Bedrock:ReorderObjects()
2637 end
2638 self:ForceDraw()
2639 return view
2640end
2641
2642function GetObject(self, name)
2643 return findObjectNamed(self, name)
2644end
2645
2646local function findObjects(view, name)
2647 local objects = {}
2648 if view and view.Children then
2649 for i, child in ipairs(view.Children) do
2650 if child.Name == name or child == name then
2651 table.insert(objects, child)
2652 elseif child.Children then
2653 local objs = findObjects(child, name)
2654 if objs then
2655 for i2, v in ipairs(objs) do
2656 table.insert(objects, v)
2657 end
2658 end
2659 end
2660 end
2661 end
2662 return objects
2663end
2664
2665function GetObjects(self, name)
2666 return findObjects(self, name)
2667end
2668
2669function RemoveObject(self, name)
2670 local obj, index, view = findObjectNamed(self, name, minI)
2671 if index then
2672 view.Children[index]:OnRemove()
2673 table.remove(view.Children, index)
2674 if view.OnUpdate then
2675 view:OnUpdate('Children')
2676 end
2677 return true
2678 end
2679 return false
2680end
2681
2682function RemoveObjects(self, name)
2683 local i = 1
2684 while self:RemoveObject(name) and i < 100 do
2685 i = i + 1
2686 end
2687
2688end
2689
2690function RemoveAllObjects(self)
2691 for i, child in ipairs(self.Children) do
2692 child:OnRemove()
2693 self.Children[i] = nil
2694 end
2695 self:ForceDraw()
2696end
2697]],
2698["Window"] = [[
2699Inherit = 'View'
2700
2701ToolBarColour = colours.lightGrey
2702ToolBarTextColour = colours.black
2703ShadowColour = colours.grey
2704Title = ''
2705Flashing = false
2706CanClose = true
2707OnCloseButton = nil
2708OldActiveObject = nil
2709
2710LoadView = function(self)
2711 local view = self:GetObject('View')
2712 if view.ToolBarColour then
2713 window.ToolBarColour = view.ToolBarColour
2714 end
2715 if view.ToolBarTextColour then
2716 window.ToolBarTextColour = view.ToolBarTextColour
2717 end
2718 view.X = 1
2719 view.Y = 2
2720
2721 view:ForceDraw()
2722 self:OnUpdate('View')
2723 if self.OnViewLoad then
2724 self.OnViewLoad(view)
2725 end
2726 self.OldActiveObject = self.Bedrock:GetActiveObject()
2727 self.Bedrock:SetActiveObject(view)
2728end
2729
2730SetView = function(self, view)
2731 self:RemoveObject('View')
2732 table.insert(self.Children, view)
2733 view.Parent = self
2734 self:LoadView()
2735end
2736
2737Flash = function(self)
2738 self.Flashing = true
2739 self:ForceDraw()
2740 self.Bedrock:StartTimer(function()self.Flashing = false end, 0.4)
2741end
2742
2743OnDraw = function(self, x, y)
2744 local toolBarColour = (self.Flashing and colours.grey or self.ToolBarColour)
2745 local toolBarTextColour = (self.Flashing and colours.black or self.ToolBarTextColour)
2746 if toolBarColour then
2747 Drawing.DrawBlankArea(x, y, self.Width, 1, toolBarColour)
2748 end
2749 if toolBarTextColour then
2750 local title = self.Bedrock.Helpers.TruncateString(self.Title, self.Width - 2)
2751 Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, title, toolBarTextColour, toolBarColour)
2752 end
2753 Drawing.IgnoreConstraint = true
2754 Drawing.DrawBlankArea(x + 1, y + 1, self.Width, self.Height, self.ShadowColour)
2755 Drawing.IgnoreConstraint = false
2756end
2757
2758Close = function(self)
2759 self.Bedrock:SetActiveObject(self.OldActiveObject)
2760 self.Bedrock.Window = nil
2761 self.Bedrock:RemoveObject(self)
2762 if self.OnClose then
2763 self:OnClose()
2764 end
2765 self = nil
2766end
2767
2768OnUpdate = function(self, value)
2769 if value == 'View' and self:GetObject('View') then
2770 self.Width = self:GetObject('View').Width
2771 self.Height = self:GetObject('View').Height + 1
2772 self.X = math.ceil((Drawing.Screen.Width - self.Width) / 2)
2773 self.Y = math.ceil((Drawing.Screen.Height - self.Height) / 2)
2774 elseif value == 'CanClose' then
2775 self:RemoveObject('CloseButton')
2776 if self.CanClose then
2777 local button = self:AddObject({X = 1, Y = 1, Width = 1, Height = 1, Type = 'Button', BackgroundColour = colours.red, TextColour = colours.white, Text = 'x', Name = 'CloseButton'})
2778 button.OnClick = function(btn)
2779 if self.OnCloseButton then
2780 self:OnCloseButton()
2781 end
2782 self:Close()
2783 end
2784 end
2785 end
2786end
2787]],
2788}
2789BasePath = ''
2790ProgramPath = nil
2791
2792function LoadAPIs(self)
2793 local function loadAPI(name, content)
2794 local env = setmetatable({}, { __index = getfenv() })
2795 local func, err = loadstring(content, name..' (Bedrock API)')
2796 if not func then
2797 return false, printError(err)
2798 end
2799 setfenv(func, env)
2800 func()
2801 local api = {}
2802 for k,v in pairs(env) do
2803 api[k] = v
2804 end
2805 _G[name] = api
2806 return true
2807 end
2808
2809 local env = getfenv()
2810 local function loadObject(name, content)
2811 loadAPI(name, content)
2812 if env[name].Inherit then
2813 if not getfenv()[env[name].Inherit] then
2814 if objects[env[name].Inherit] then
2815 loadObject(env[name].Inherit, objects[env[name].Inherit])
2816 elseif fs.exists(self.ProgramPath..'/Objects/'..env[name].Inherit..'.lua') then
2817 local h = fs.open(self.ProgramPath..'/Objects/'..env[name].Inherit..'.lua', 'r')
2818 loadObject(env[name].Inherit, h.readAll())
2819 h.close()
2820 loadObject(name, content)
2821 return
2822 end
2823 end
2824 env[name].__index = getfenv()[env[name].Inherit]
2825 else
2826 env[name].__index = Object
2827 end
2828 setmetatable(env[name], env[name])
2829 end
2830
2831 for k, v in pairs(apis) do
2832 loadAPI(k, v)
2833 if k == 'Helpers' then
2834 self.Helpers = Helpers
2835 end
2836 end
2837
2838 for k, v in pairs(objects) do
2839 loadObject(k, v)
2840 end
2841
2842 local privateObjPath = self.ProgramPath..'/Objects/'
2843 if fs.exists(privateObjPath) and fs.isDir(privateObjPath) then
2844 for i, v in ipairs(fs.list(privateObjPath)) do
2845 if v ~= '.DS_Store' then
2846 local name = string.match(v, '(%w+)%.?.-')
2847 local h = fs.open(privateObjPath..v, 'r')
2848 loadObject(name, h.readAll())
2849 h.close()
2850 end
2851 end
2852 end
2853
2854 local privateAPIPath = self.ProgramPath..'/APIs/'
2855 if fs.exists(privateAPIPath) and fs.isDir(privateAPIPath) then
2856 for i, v in ipairs(fs.list(privateAPIPath)) do
2857 if v ~= '.DS_Store' then
2858 local name = string.match(v, '(%w+)%.?.-')
2859 local h = fs.open(privateAPIPath..v, 'r')
2860 loadAPI(name, h.readAll())
2861 h.close()
2862 end
2863 end
2864 end
2865end
2866
2867AllowTerminate = true
2868
2869View = nil
2870Menu = nil
2871
2872ActiveObject = nil
2873
2874DrawTimer = nil
2875DrawTimerExpiry = 0
2876
2877IsDrawing = false
2878
2879Running = true
2880
2881DefaultView = 'main'
2882
2883AnimationEnabled = true
2884
2885EventHandlers = {
2886
2887}
2888
2889ObjectClickHandlers = {
2890
2891}
2892
2893ObjectUpdateHandlers = {
2894
2895}
2896
2897Timers = {
2898
2899}
2900
2901ModifierKeys = {}
2902KeyboardShortcuts = {}
2903
2904keys.leftCommand = 219
2905keys.rightCommand = 220
2906
2907function Initialise(self, programPath)
2908 self.ProgramPath = programPath or self.ProgramPath
2909 if not programPath then
2910 if self.ProgramPath then
2911 local prgPath = self.ProgramPath
2912 local prgName = fs.getName(prgPath)
2913 if prgPath:find('/') then
2914 self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1)
2915 self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1)
2916 else
2917 self.ProgramPath = ''
2918 end
2919 else
2920 self.ProgramPath = ''
2921 end
2922 end
2923 self:LoadAPIs()
2924 self.ViewPath = self.ProgramPath .. '/Views/'
2925 --first, check that the barebones APIs are available
2926 local requiredApis = {
2927 'Drawing',
2928 'View'
2929 }
2930 local env = getfenv()
2931 for i,v in ipairs(requiredApis) do
2932 if not env[v] then
2933 error('The API: '..v..' is not loaded. Please make sure you load it to use Bedrock.')
2934 end
2935 end
2936
2937 local copy = { }
2938 for k, v in pairs(self) do
2939 if k ~= 'Initialise' then
2940 copy[k] = v
2941 end
2942 end
2943 return setmetatable(copy, getmetatable(self))
2944end
2945
2946function HandleClick(self, event, side, x, y)
2947 if self.Menu then
2948 if not self.View:DoClick(self.Menu, event, side, x, y) then
2949 self.Menu:Close()
2950 end
2951 elseif self.Window then
2952 if not self.View:CheckClick(self.Window, x, y) then
2953 self.Window:Flash()
2954 else
2955 self.View:DoClick(self.Window, event, side, x, y)
2956 end
2957 elseif self.View then
2958 if self.View:Click(event, side, x, y) ~= false then
2959 end
2960 end
2961end
2962
2963function UnregisterKeyboardShortcut(self, name)
2964 if name then
2965 self.KeyboardShortcuts[name] = nil
2966 end
2967end
2968
2969function RegisterKeyboardShortcut(self, keys, func, name)
2970 name = name or tostring(math.random(1, 10000))
2971 if type(keys[1]) == 'table' then
2972 for i, v in ipairs(keys) do
2973 self.KeyboardShortcuts[name] = {Keys = v, Function = func}
2974 end
2975 else
2976 self.KeyboardShortcuts[name] = {Keys = keys, Function = func}
2977 end
2978 return name
2979end
2980
2981function TryKeyboardShortcuts(self, keychar)
2982 if keychar == keys.backspace then
2983 keychar = keys.delete
2984 end
2985
2986 local len = 1 -- + keychar
2987 for k, v in pairs(self.ModifierKeys) do
2988 len = len + 1
2989 end
2990
2991 if type(keychar) == 'string' then
2992 keychar = keychar:lower()
2993 end
2994
2995 for _, shortcut in pairs(self.KeyboardShortcuts) do
2996 local match = true
2997 for i2, key in ipairs(shortcut.Keys) do
2998 if type(keychar) == 'string' then
2999 keychar = keychar:lower()
3000 end
3001
3002 if self.ModifierKeys[key] == nil and key ~= keychar then
3003 match = false
3004 end
3005 end
3006
3007 if match and #shortcut.Keys == len then
3008 return shortcut.Function() ~= false
3009 end
3010 end
3011end
3012
3013function HandleKeyChar(self, event, keychar)
3014 if keychar == keys.leftCtrl or keychar == keys.leftShift or keychar == keys.leftAlt or keychar == keys.leftCommand or keychar == keys.rightCommand or keychar == keys.rightCtrl or keychar == keys.rightShift or keychar == keys.rightAlt then
3015 if keychar == keys.leftCommand or keychar == keys.rightCommand or keychar == keys.rightCtrl then
3016 keychar = keys.leftCtrl
3017 elseif keychar == keys.rightAlt then
3018 keychar = keys.leftAlt
3019 elseif keychar == keys.rightShift then
3020 keychar = keys.leftShift
3021 end
3022 self.ModifierKeys[keychar] = self:StartTimer(function(_, timer)
3023 if timer == self.ModifierKeys[keychar] then
3024 self.ModifierKeys[keychar] = nil
3025 end
3026 end, 1)
3027 elseif self:TryKeyboardShortcuts(keychar) then
3028 return
3029 end
3030
3031 if self:GetActiveObject() then
3032 local activeObject = self:GetActiveObject()
3033 if activeObject.OnKeyChar then
3034 if activeObject:OnKeyChar(event, keychar) ~= false then
3035 --self:Draw()
3036 end
3037 end
3038 end
3039end
3040
3041PreparedMenus = {}
3042
3043function PrepareMenu(self, name)
3044 local menu = self:AddObject(name, {Type = 'Menu', X = 1, Y = 1, Prepared = true})
3045 menu.Visible = false
3046 self.PreparedMenus[name] = menu
3047 return menu
3048end
3049
3050function ToggleMenu(self, name, owner, x, y)
3051 if self.Menu then
3052 self.Menu:Close()
3053 return false
3054 else
3055 self:SetMenu(name, owner, x, y)
3056 return true
3057 end
3058end
3059
3060function SetMenu(self, menu, owner, x, y)
3061 x = x or 1
3062 y = y or 1
3063 if self.Menu then
3064 self.Menu:Close()
3065 end
3066 if menu then
3067 local pos = owner:GetPosition()
3068 if self.PreparedMenus[menu] then
3069 self.Menu = self.PreparedMenus[menu]
3070 self.Menu.Visible = true
3071 self.Menu.Owner = owner
3072 self.Menu.X = pos.X + x - 1
3073 self.Menu.Y = pos.Y + y
3074 self.Menu.Z = self.View.Children[#self.View.Children].Z + 1
3075 self:ReorderObjects()
3076 else
3077 self.Menu = self:AddObject(menu, {Type = 'Menu', Owner = owner, X = pos.X + x - 1, Y = pos.Y + y, Z = self.View.Children[#self.View.Children].Z + 1})
3078 end
3079 end
3080end
3081
3082function ObjectClick(self, name, func)
3083 self.ObjectClickHandlers[name] = func
3084end
3085
3086function ClickObject(self, object, event, side, x, y)
3087 if self.ObjectClickHandlers[object.Name] then
3088 return self.ObjectClickHandlers[object.Name](object, event, side, x, y)
3089 end
3090 return false
3091end
3092
3093function ObjectUpdate(self, name, func)
3094 self.ObjectUpdateHandlers[name] = func
3095end
3096
3097function UpdateObject(self, object, ...)
3098 if self.ObjectUpdateHandlers[object.Name] then
3099 self.ObjectUpdateHandlers[object.Name](object, ...)
3100 --self:Draw()
3101 end
3102end
3103
3104function GetAbsolutePosition(self, obj)
3105 if not obj.Parent then
3106 return {X = obj.X, Y = obj.Y}
3107 else
3108 local pos = self:GetAbsolutePosition(obj.Parent)
3109 local x = pos.X + obj.X - 1
3110 local y = pos.Y + obj.Y - 1
3111 if not obj.Fixed and obj.Parent.ChildOffset then
3112 x = x + obj.Parent.ChildOffset.X
3113 y = y + obj.Parent.ChildOffset.Y
3114 end
3115 return {X = x, Y = y}
3116 end
3117end
3118
3119function LoadView(self, name, draw)
3120 if self.View and self.OnViewClose then
3121 self.OnViewClose(self.View.Name)
3122 end
3123 if self.View then
3124 self.View:OnRemove()
3125 end
3126 local success = false
3127
3128 if Drawing.Screen.Width <= 26 and fs.exists(self.ViewPath..name..'-pocket.view') then
3129 name = name..'-pocket'
3130 elseif Drawing.Screen.Width <= 39 and fs.exists(self.ViewPath..name..'-turtle.view') then
3131 name = name..'-turtle'
3132 end
3133
3134 if not fs.exists(self.ViewPath..name..'.view') then
3135 error('The view: '..name..'.view does not exist.')
3136 end
3137
3138 local h = fs.open(self.ViewPath..name..'.view', 'r')
3139 if h then
3140 local view = textutils.unserialize(h.readAll())
3141 h.close()
3142 if view then
3143 self.View = View:InitialiseFile(self, view, name)
3144 self:ReorderObjects()
3145
3146 if OneOS and view.ToolBarColour then
3147 OneOS.ToolBarColour = view.ToolBarColour
3148 end
3149 if OneOS and view.ToolBarTextColour then
3150 OneOS.ToolBarTextColour = view.ToolBarTextColour
3151 end
3152 if not self:GetActiveObject() then
3153 self:SetActiveObject()
3154 end
3155 success = true
3156 end
3157 end
3158
3159 if success and self.OnViewLoad then
3160 self.OnViewLoad(name)
3161 end
3162
3163 if draw ~= false then
3164 self:Draw()
3165 end
3166
3167 if not success then
3168 error('Failed to load view: '..name..'. It probably isn\'t formatted correctly. Did you forget a } or ,?')
3169 end
3170
3171 return success
3172end
3173
3174function InheritFile(self, file, name)
3175 local h = fs.open(self.ViewPath..name..'.view', 'r')
3176 if h then
3177 local super = textutils.unserialize(h.readAll())
3178 if super then
3179 if type(super) ~= 'table' then
3180 error('View: "'..name..'.view" is not formatted correctly.')
3181 end
3182
3183 for k, v in pairs(super) do
3184 if not file[k] then
3185 file[k] = v
3186 end
3187 end
3188 return file
3189 end
3190 end
3191 return file
3192end
3193
3194function ParseStringSize(self, parent, k, v)
3195 local parentSize = parent.Width
3196 if k == 'Height' or k == 'Y' then
3197 parentSize = parent.Height
3198 end
3199 local parts = {v}
3200 if type(v) == 'string' and string.find(v, ',') then
3201 parts = {}
3202 for word in string.gmatch(v, '([^,]+)') do
3203 table.insert(parts, word)
3204 end
3205 end
3206
3207 v = 0
3208 for i2, part in ipairs(parts) do
3209 if type(part) == 'string' and part:sub(#part) == '%' then
3210 v = v + math.ceil(parentSize * (tonumber(part:sub(1, #part-1)) / 100))
3211 else
3212 v = v + tonumber(part)
3213 end
3214 end
3215 return v
3216end
3217
3218function ObjectFromFile(self, file, view)
3219 local env = getfenv()
3220 if env[file.Type] then
3221 if not env[file.Type].Initialise then
3222 error('Malformed Object: '..file.Type)
3223 end
3224 local object = {}
3225
3226 if file.InheritView then
3227 file = self:InheritFile(file, file.InheritView)
3228 end
3229
3230 object.AutoWidth = true
3231 for k, v in pairs(file) do
3232 if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then
3233 v = self:ParseStringSize(view, k, v)
3234 end
3235
3236 if k == 'Width' then
3237 object.AutoWidth = false
3238 end
3239 if k ~= 'Children' then
3240 object[k] = v
3241 else
3242 object[k] = {}
3243 end
3244 end
3245
3246 object.Parent = view
3247 object.Bedrock = self
3248 if not object.Name then
3249 object.Name = file.Type
3250 end
3251
3252 object = env[file.Type]:Initialise(object)
3253
3254 if file.Children then
3255 for i, obj in ipairs(file.Children) do
3256 local _view = self:ObjectFromFile(obj, object)
3257 if not _view.Z then
3258 _view.Z = i
3259 end
3260 _view.Parent = object
3261 table.insert(object.Children, _view)
3262 end
3263 end
3264
3265 if not object.OnClick then
3266 object.OnClick = function(...) return self:ClickObject(...) end
3267 end
3268 --object.OnUpdate = function(...) self:UpdateObject(...) end
3269
3270 if object.OnUpdate then
3271 for k, v in pairs(env[file.Type]) do
3272 object:OnUpdate(k)
3273 end
3274
3275 for k, v in pairs(object.__index) do
3276 object:OnUpdate(k)
3277 end
3278 end
3279
3280 if object.Active then
3281 object.Bedrock:SetActiveObject(object)
3282 end
3283 if object.OnLoad then
3284 object:OnLoad()
3285 end
3286 object.Ready = true
3287 return object
3288 elseif not file.Type then
3289 error('No object type specified. (e.g. Type = "Button")')
3290 else
3291 error('No Object: '..file.Type..'. The API probably isn\'t loaded')
3292 end
3293end
3294
3295function ReorderObjects(self)
3296 if self.View and self.View.Children then
3297 self.View:ReorderObjects()
3298 end
3299end
3300
3301function AddObject(self, info, extra, first)
3302 return self.View:AddObject(info, extra, first)
3303end
3304
3305function GetObject(self, name)
3306 return self.View:GetObject(name)
3307end
3308
3309function GetObjects(self, name)
3310 return self.View:GetObjects(name)
3311end
3312
3313function RemoveObject(self, name)
3314 return self.View:RemoveObject(name)
3315end
3316
3317function RemoveObjects(self, name)
3318 return self.View:RemoveObjects(name)
3319end
3320
3321DrawEvent = nil
3322
3323function HandleDraw(self, event, id)
3324 if id == self.DrawEvent then
3325 self.DrawEvent = nil
3326 self:Draw()
3327 end
3328end
3329
3330function ForceDraw(self)
3331 if not self.DrawEvent then--or self.DrawTimerExpiry <= os.clock() then
3332 self.DrawEvent = math.random()
3333 os.queueEvent('bedrock_draw', self.DrawEvent)
3334 -- self:StartTimer(function()
3335 -- self.DrawTimer = nil
3336 -- self:Draw()
3337 -- end, 0.05)
3338 -- self.DrawTimerExpiry = os.clock() + 0.1
3339 end
3340end
3341
3342function DisplayWindow(self, _view, title, canClose)
3343 if canClose == nil then
3344 canClose = true
3345 end
3346 if type(_view) == 'string' then
3347 local h = fs.open(self.ViewPath.._view..'.view', 'r')
3348 if h then
3349 _view = textutils.unserialize(h.readAll())
3350 h.close()
3351 end
3352 end
3353
3354 self.Window = self:AddObject({Type = 'Window', Z = 999, Title = title, CanClose = canClose})
3355 _view.Type = 'View'
3356 _view.Name = 'View'
3357 _view.Z = 1
3358 _view.BackgroundColour = _view.BackgroundColour or colours.white
3359 self.Window:SetView(self:ObjectFromFile(_view, self.Window))
3360end
3361
3362function DisplayAlertWindow(self, title, text, buttons, callback)
3363 local func = function(btn)
3364 self.Window:Close()
3365 if callback then
3366 callback(btn.Text)
3367 end
3368 end
3369 local children = {}
3370 local usedX = -1
3371 if buttons then
3372 for i, text in ipairs(buttons) do
3373 usedX = usedX + 3 + #text
3374 table.insert(children, {
3375 ["Y"]="100%,-1",
3376 ["X"]="100%,-"..usedX,
3377 ["Name"]=text.."Button",
3378 ["Type"]="Button",
3379 ["Text"]=text,
3380 OnClick = func
3381 })
3382 end
3383 end
3384
3385 local width = usedX + 2
3386 if width < 28 then
3387 width = 28
3388 end
3389
3390 local canClose = true
3391 if buttons and #buttons~=0 then
3392 canClose = false
3393 end
3394
3395 local height = 0
3396 if text then
3397 height = #Helpers.WrapText(text, width - 2)
3398 table.insert(children, {
3399 ["Y"]=2,
3400 ["X"]=2,
3401 ["Width"]="100%,-2",
3402 ["Height"]=height,
3403 ["Name"]="Label",
3404 ["Type"]="Label",
3405 ["Text"]=text
3406 })
3407 end
3408 local view = {
3409 Children = children,
3410 Width=width,
3411 Height=3+height+(canClose and 0 or 1),
3412 OnKeyChar = function(_view, keychar)
3413 func({Text=buttons[1]})
3414 end
3415 }
3416 self:DisplayWindow(view, title, canClose)
3417end
3418
3419function DisplayTextBoxWindow(self, title, text, callback, textboxText, cursorAtEnd)
3420 textboxText = textboxText or ''
3421 local children = {
3422 {
3423 ["Y"]="100%,-1",
3424 ["X"]="100%,-4",
3425 ["Name"]="OkButton",
3426 ["Type"]="Button",
3427 ["Text"]="Ok",
3428 OnClick = function()
3429 local text = self.Window:GetObject('TextBox').Text
3430 self.Window:Close()
3431 callback(true, text)
3432 end
3433 },
3434 {
3435 ["Y"]="100%,-1",
3436 ["X"]="100%,-13",
3437 ["Name"]="CancelButton",
3438 ["Type"]="Button",
3439 ["Text"]="Cancel",
3440 OnClick = function()
3441 self.Window:Close()
3442 callback(false)
3443 end
3444 }
3445 }
3446
3447 local height = -1
3448 if text and #text ~= 0 then
3449 height = #Helpers.WrapText(text, 26)
3450 table.insert(children, {
3451 ["Y"]=2,
3452 ["X"]=2,
3453 ["Width"]="100%,-2",
3454 ["Height"]=height,
3455 ["Name"]="Label",
3456 ["Type"]="Label",
3457 ["Text"]=text
3458 })
3459 end
3460 table.insert(children,
3461 {
3462 ["Y"]=3+height,
3463 ["X"]=2,
3464 ["Width"]="100%,-2",
3465 ["Name"]="TextBox",
3466 ["Type"]="TextBox",
3467 ["Text"]=textboxText,
3468 ["CursorPos"]=(cursorAtEnd and #textboxText or 0)
3469 })
3470 local view = {
3471 Children = children,
3472 Width=28,
3473 Height=5+height+(canClose and 0 or 1),
3474 }
3475 self:DisplayWindow(view, title)
3476 self.Window:GetObject('TextBox').OnChange = function(txtbox, event, keychar)
3477 if keychar == keys.enter then
3478 self.Window:Close()
3479 callback(true, txtbox.Text)
3480 end
3481 end
3482 self:SetActiveObject(self.Window:GetObject('TextBox'))
3483 self.Window.OnCloseButton = function()callback(false)end
3484end
3485
3486local function isFolder(path)
3487 local _fs = fs
3488 if OneOS then
3489 _fs = OneOS.FS
3490 end
3491
3492 if not _fs.isDir(path) then
3493 return false
3494 end
3495
3496 local extension
3497 if OneOS and OneOS.System then
3498 extension = OneOS.System.RealExtension(path)
3499 elseif self.System then
3500 extension = self.System.RealExtension(path)
3501 else
3502 extension = self.Helpers.Extension(path)
3503 end
3504
3505 return extension == ''
3506end
3507
3508function DisplayOpenFileWindow(self, title, callback)
3509 title = title or 'Open File'
3510 local func = function(btn)
3511 self.Window:Close()
3512 if callback then
3513 callback(btn.Text)
3514 end
3515 end
3516
3517 local sidebarItems = {}
3518
3519 --this is a really, really super bad way of doing it
3520 local separator = ' !'
3521
3522 local _fs = fs
3523 if OneOS then
3524 _fs = OneOS.FS
3525 end
3526
3527 local function addFolder(path, level)
3528 for i, v in ipairs(_fs.list(path)) do
3529 local fPath = path .. '/' .. v
3530 if fPath ~= '/rom' and fPath ~= '/Favourites' and fPath ~= '/.git' and _fs.isDir(fPath) then
3531 if isFolder(fPath) then
3532 table.insert(sidebarItems, level .. v..separator..fPath)
3533 addFolder(fPath, level .. ' ')
3534 end
3535 end
3536 end
3537 end
3538
3539 if OneOS then
3540 _fs = OneOS.FS
3541 end
3542
3543 addFolder('','')
3544
3545 local currentFolder = ''
3546 local selectedPath = nil
3547
3548 local goToFolder = nil
3549
3550 local children = {
3551 {
3552 ["Y"]="100%,-2",
3553 ["X"]=1,
3554 ["Height"]=3,
3555 ["Width"]="100%",
3556 ["BackgroundColour"]=colours.lightGrey,
3557 ["Name"]="SidebarListView",
3558 ["Type"]="View"
3559 },
3560 {
3561 ["Y"]="100%,-1",
3562 ["X"]="100%,-4",
3563 ["Name"]="OkButton",
3564 ["Type"]="Button",
3565 ["Text"]="Ok",
3566 ["BackgroundColour"]=colours.white,
3567 ["Enabled"]=false,
3568 OnClick = function()
3569 if selectedPath then
3570 self.Window:Close()
3571 callback(true, Helpers.TidyPath(selectedPath))
3572 end
3573 end
3574 },
3575 {
3576 ["Y"]="100%,-1",
3577 ["X"]="100%,-13",
3578 ["Name"]="CancelButton",
3579 ["Type"]="Button",
3580 ["Text"]="Cancel",
3581 ["BackgroundColour"]=colours.white,
3582 OnClick = function()
3583 self.Window:Close()
3584 callback(false)
3585 end
3586 },
3587 {
3588 ["Y"]=1,
3589 ["X"]=1,
3590 ["Height"]="100%,-3",
3591 ["Width"]="40%,-1",
3592 ["Name"]="SidebarListView",
3593 ["Type"]="ListView",
3594 ["CanSelect"]=true,
3595 ["Items"]={
3596 ["Computer"] = sidebarItems
3597 },
3598 OnSelect = function(listView, text)
3599 local _,s = text:find(separator)
3600 if s then
3601 local path = text:sub(s + 1)
3602 goToFolder(path)
3603 end
3604 end,
3605 OnClick = function(listView, event, side, x, y)
3606 if y == 1 then
3607 goToFolder('/')
3608 end
3609 end
3610 },
3611 {
3612 ["Y"]=1,
3613 ["X"]="40%",
3614 ["Height"]="100%,-3",
3615 ["Width"]=1,
3616 ["Type"]="Separator"
3617 },
3618 {
3619 ["Y"]=1,
3620 ["X"]="40%,2",
3621 ["Width"]="65%,-3",
3622 ["Height"]=1,
3623 ["Type"]="Label",
3624 ["Name"]="PathLabel",
3625 ["TextColour"]=colours.lightGrey,
3626 ["Text"]='/'
3627 },
3628 {
3629 ["Y"]=2,
3630 ["X"]="40%,1",
3631 ["Height"]="100%,-4",
3632 ["Width"]="65%,-1",
3633 ["Name"]="FilesListView",
3634 ["Type"]="ListView",
3635 ["CanSelect"]=true,
3636 ["Items"]={},
3637 OnSelect = function(listView, text)
3638 selectedPath = Helpers.TidyPath(currentFolder .. '/' .. text)
3639 self.Window:GetObject('OkButton').Enabled = true
3640 end,
3641 OnClick = function(listView, event, side, x, y)
3642 if y == 1 then
3643 goToFolder('/')
3644 end
3645 end
3646 },
3647 }
3648 local view = {
3649 Children = children,
3650 Width=40,
3651 Height= Drawing.Screen.Height - 4,
3652 OnCloseButton=function()
3653 callback(false)
3654 end
3655 }
3656 self:DisplayWindow(view, title)
3657
3658 goToFolder = function(path)
3659 path = Helpers.TidyPath(path)
3660 self.Window:GetObject('PathLabel').Text = path
3661 currentFolder = path
3662
3663 local filesListItems = {}
3664 for i, v in ipairs(_fs.list(path)) do
3665 if not isFolder(v .. '/' .. path) then
3666 table.insert(filesListItems, v)
3667 end
3668 end
3669 self.Window:GetObject('OkButton').Enabled = false
3670 selectedPath = nil
3671 self.Window:GetObject('FilesListView').Items = filesListItems
3672
3673 end
3674
3675 if startPath then
3676 goToFolder(startPath)
3677 elseif OneOS then
3678 goToFolder('/Desktop/Documents/')
3679 else
3680 goToFolder('')
3681 end
3682
3683 self.Window.OnCloseButton = function()callback(false)end
3684end
3685
3686function DisplaySaveFileWindow(self, title, callback, extension, startPath)
3687 local _fs = fs
3688 if extension and extension:sub(1,1) ~= '.' then
3689 extension = '.' .. extension
3690 end
3691 extension = extension or ''
3692
3693 title = title or 'Save File'
3694 local func = function(btn)
3695 self.Window:Close()
3696 if callback then
3697 callback(btn.Text)
3698 end
3699 end
3700
3701 local sidebarItems = {}
3702
3703 --this is a really, really super bad way of doing it
3704 local separator = ' !'
3705
3706 local function addFolder(path, level)
3707 for i, v in ipairs(_fs.list(path)) do
3708 local fPath = path .. '/' .. v
3709 if fPath ~= '/rom' and fPath ~= '/Favourites' and fPath ~= '/.git' and _fs.isDir(fPath) then
3710 if isFolder(fPath) then
3711 table.insert(sidebarItems, level .. v..separator..fPath)
3712 addFolder(fPath, level .. ' ')
3713 end
3714 end
3715 end
3716 end
3717
3718 if OneOS then
3719 _fs = OneOS.FS
3720 end
3721 addFolder('','')
3722
3723 local currentFolder = ''
3724 local selectedPath = nil
3725
3726 local goToFolder = nil
3727
3728 local function updatePath()
3729 local text = self:GetObject('FileNameTextBox').Text
3730 if #text == 0 then
3731 self.Window:GetObject('OkButton').Enabled = false
3732 selectedPath = Helpers.TidyPath(currentFolder)
3733 else
3734 self.Window:GetObject('OkButton').Enabled = true
3735 selectedPath = Helpers.TidyPath(currentFolder .. '/' .. text .. extension)
3736 end
3737 self:GetObject('PathLabel').Text = selectedPath
3738 end
3739
3740 local children = {
3741 {
3742 ["Y"]="100%,-2",
3743 ["X"]=1,
3744 ["Height"]=3,
3745 ["Width"]="100%",
3746 ["BackgroundColour"]=colours.lightGrey,
3747 ["Type"]="View"
3748 },
3749 {
3750 ["Y"]="100%,-1",
3751 ["X"]="100%,-4",
3752 ["Name"]="OkButton",
3753 ["Type"]="Button",
3754 ["Text"]="Ok",
3755 ["BackgroundColour"]=colours.white,
3756 ["Enabled"]=false,
3757 OnClick = function()
3758 if selectedPath then
3759 local text = self:GetObject('FileNameTextBox').Text
3760 self.Window:Close()
3761 callback(true, selectedPath, text)
3762 end
3763 end
3764 },
3765 {
3766 ["Y"]="100%,-1",
3767 ["X"]="100%,-13",
3768 ["Name"]="CancelButton",
3769 ["Type"]="Button",
3770 ["Text"]="Cancel",
3771 ["BackgroundColour"]=colours.white,
3772 OnClick = function()
3773 self.Window:Close()
3774 callback(false)
3775 end
3776 },
3777 {
3778 ["Y"]="100%,-2",
3779 ["X"]=3,
3780 ["Width"]="100%,-4",
3781 ["Name"]="PathLabel",
3782 ["Type"]="Label",
3783 ["Text"]="/",
3784 ["TextColour"]=colours.grey
3785 },
3786 {
3787 ["Y"]="100%,-1",
3788 ["X"]=3,
3789 ["Width"]="100%,-17",
3790 ["Name"]="FileNameTextBox",
3791 ["Type"]="TextBox",
3792 ["Placeholder"]="File Name",
3793 ["Active"]=true,
3794 ["BackgroundColour"]=colours.white,
3795 OnChange = function(_self, event, keychar)
3796 if keychar == keys.enter then
3797 self:GetObject('OkButton'):OnClick()
3798 else
3799 updatePath()
3800 end
3801 end
3802 },
3803 {
3804 ["Y"]=1,
3805 ["X"]=2,
3806 ["Height"]="100%,-3",
3807 ["Width"]="100%,-1",
3808 ["Name"]="SidebarListView",
3809 ["Type"]="ListView",
3810 ["CanSelect"]=true,
3811 ["Items"]={
3812 ["Computer"] = sidebarItems
3813 },
3814 OnSelect = function(listView, text)
3815 local _,s = text:find(separator)
3816 if s then
3817 local path = text:sub(s + 1)
3818 goToFolder(path)
3819 end
3820 end,
3821 OnClick = function(listView, event, side, x, y)
3822 if y == 1 then
3823 goToFolder('/')
3824 end
3825 end
3826 },
3827 }
3828 local view = {
3829 Children = children,
3830 Width=35,
3831 Height= Drawing.Screen.Height - 4,
3832 OnCloseButton=function()
3833 callback(false)
3834 end
3835 }
3836 self:DisplayWindow(view, title)
3837
3838 self:SetActiveObject(self.Window:GetObject('FileNameTextBox'))
3839
3840 goToFolder = function(path)
3841 path = Helpers.TidyPath(path)
3842 currentFolder = path
3843 selectedPath = nil
3844 updatePath()
3845 end
3846
3847 if startPath then
3848 goToFolder(startPath)
3849 elseif OneOS then
3850 goToFolder('/Desktop/Documents/')
3851 else
3852 goToFolder('')
3853 end
3854
3855 self.Window.OnCloseButton = function()callback(false)end
3856end
3857
3858function RegisterEvent(self, event, func)
3859 if not self.EventHandlers[event] then
3860 self.EventHandlers[event] = {}
3861 end
3862 table.insert(self.EventHandlers[event], func)
3863end
3864
3865function StartRepeatingTimer(self, func, interval)
3866 local int = interval
3867 if type(int) == 'function' then
3868 int = int()
3869 end
3870 if not int or int <= 0 then
3871 return
3872 end
3873 local timer = os.startTimer(int)
3874
3875 self.Timers[timer] = {func, true, interval}
3876 return timer
3877end
3878
3879function StartTimer(self, func, delay)
3880 local timer = os.startTimer(delay)
3881 self.Timers[timer] = {func, false}
3882 return timer
3883end
3884
3885function StopTimer(self, timer)
3886 if self.Timers[timer] then
3887 self.Timers[timer] = nil
3888 end
3889end
3890
3891function HandleTimer(self, event, timer)
3892 if self.Timers[timer] then
3893 local oldTimer = self.Timers[timer]
3894 self.Timers[timer] = nil
3895 local new = nil
3896 if oldTimer[2] then
3897 new = self:StartRepeatingTimer(oldTimer[1], oldTimer[3])
3898 end
3899 if oldTimer and oldTimer[1] then
3900 oldTimer[1](new, timer)
3901 end
3902 elseif self.OnTimer then
3903 self.OnTimer(self, event, timer)
3904 end
3905end
3906
3907function HandlePaste(self, event, text)
3908 local activeObject = self:GetActiveObject()
3909 if activeObject and activeObject.OnPaste then
3910 activeObject:OnPaste(event, text)
3911 end
3912end
3913
3914function SetActiveObject(self, object)
3915 if object then
3916 if object ~= self.ActiveObject then
3917 self.ActiveObject = object
3918 object:ForceDraw()
3919 end
3920 elseif self.ActiveObject ~= nil then
3921 self.ActiveObject = nil
3922 self.CursorPos = nil
3923 self.View:ForceDraw()
3924 end
3925end
3926
3927function GetActiveObject(self)
3928 return self.ActiveObject
3929end
3930
3931OnTimer = nil
3932OnClick = nil
3933OnKeyChar = nil
3934OnDrag = nil
3935OnScroll = nil
3936OnViewLoad = nil
3937OnViewClose = nil
3938OnDraw = nil
3939OnQuit = nil
3940
3941local eventFuncs = {
3942 OnClick = {'mouse_click', 'monitor_touch'},
3943 OnKeyChar = {'key', 'char'},
3944 OnDrag = {'mouse_drag'},
3945 OnScroll = {'mouse_scroll'},
3946 HandleClick = {'mouse_click', 'mouse_drag', 'mouse_scroll', 'monitor_touch'},
3947 HandleKeyChar = {'key', 'char'},
3948 HandlePaste = {'paste'},
3949 HandleTimer = {'timer'},
3950 HandleDraw = {'bedrock_draw'}
3951}
3952
3953local drawCalls = 0
3954local ignored = 0
3955function Draw(self)
3956 self.IsDrawing = true
3957 if self.OnDraw then
3958 self:OnDraw()
3959 end
3960
3961 if self.View and self.View:NeedsDraw() then
3962 self.View:Draw()
3963 Drawing.DrawBuffer()
3964 if isDebug then
3965 drawCalls = drawCalls + 1
3966 end
3967 elseif not self.View then
3968 print('No loaded view. You need to do program:LoadView first.')
3969 end
3970
3971 if self:GetActiveObject() and self.CursorPos and type(self.CursorPos[1]) == 'number' and type(self.CursorPos[2]) == 'number' and self.CursorPos[1] >= 1 and self.CursorPos[1] <= Drawing.Screen.Width and self.CursorPos[2] >= 1 and self.CursorPos[2] <= Drawing.Screen.Height then
3972 term.setCursorPos(self.CursorPos[1], self.CursorPos[2])
3973 term.setTextColour(self.CursorColour)
3974 term.setCursorBlink(true)
3975 else
3976 term.setCursorBlink(false)
3977 end
3978
3979 self.IsDrawing = false
3980end
3981
3982function EventHandler(self)
3983 local event = { os.pullEventRaw() }
3984
3985 if self.EventHandlers[event[1]] then
3986 for i, e in ipairs(self.EventHandlers[event[1]]) do
3987 e(self, unpack(event))
3988 end
3989 end
3990end
3991
3992function Quit(self)
3993 self.Running = false
3994end
3995
3996function Run(self, ready)
3997 -- Bypass the window API
3998 -- This doesn't really work...
3999 -- term.redirect(term.native())
4000 -- if term.native then
4001 -- if type(term.native) == 'function' then
4002 -- term.redirect(term.native())
4003 -- else
4004 -- term.redirect(term.native)
4005 -- end
4006 -- end
4007
4008 if not term.isColour or not term.isColour() then
4009 print('This program requires an advanced (golden) comptuer to run, sorry.')
4010 error('', 0)
4011 end
4012
4013 for name, events in pairs(eventFuncs) do
4014 if self[name] then
4015 for i, event in ipairs(events) do
4016 self:RegisterEvent(event, self[name])
4017 end
4018 end
4019 end
4020
4021 if self.AllowTerminate then
4022 self:RegisterEvent('terminate', function()error('Terminated', 0) end)
4023 end
4024
4025 if self.DefaultView and self.DefaultView ~= '' and fs.exists(self.ViewPath..self.DefaultView..'.view') then
4026 self:LoadView(self.DefaultView)
4027 end
4028
4029 if ready then
4030 ready()
4031 end
4032
4033 self:Draw()
4034
4035 while self.Running do
4036 self:EventHandler()
4037 end
4038
4039 if self.OnQuit then
4040 self:OnQuit()
4041 end
4042end