SHOW:
|
|
- or go back to the newest paste.
1 | -- Copyright Thomas Bogue 2013 | |
2 | -- This program is free software: you can redistribute it and/or modify | |
3 | -- it under the terms of the GNU Lesser General Public License as published by | |
4 | -- the Free Software Foundation, either version 3 of the License, or | |
5 | -- (at your option) any later version. | |
6 | ||
7 | -- This program is distributed in the hope that it will be useful, | |
8 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | -- GNU Lesser General Public License for more details. | |
11 | ||
12 | -- You should have received a copy of the GNU Lesser General Public License | |
13 | -- along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | ||
15 | monitor=nil | |
16 | defaultButtonBackground=colors.blue | |
17 | defaultButtonForeground=colors.white | |
18 | terminalBackground=colors.black | |
19 | defaultSliderForeground=colors.white | |
20 | defaultSliderBackground=colors.gray | |
21 | defaultFlagMonitorForeground=colors.white | |
22 | defaultProgressBarForeground=colors.white | |
23 | defaultProgressBarBackground=colors.gray | |
24 | redrawNeeded=false | |
25 | ||
26 | function version() | |
27 | return "widget library v1.1" | |
28 | end | |
29 | ||
30 | local function nop() | |
31 | end | |
32 | ||
33 | function getMonitor() | |
34 | return monitor | |
35 | end | |
36 | ||
37 | function setMonitor(m) | |
38 | monitor=m | |
39 | end | |
40 | ||
41 | function getDefaultMonitor() | |
42 | --print("searching for advanced monitor...") | |
43 | facings={"top","bottom","right","left","front","back"} | |
44 | for key,facing in pairs(facings) do | |
45 | mon=peripheral.wrap(facing) | |
46 | --print("looking for an advanced monitor " .. facing .. " " .. tostring(mon)) | |
47 | if (mon~=nil) then | |
48 | --print("peripheral found") | |
49 | if (mon.setTextColor~=nil) then | |
50 | --print("monitor found on ".. facing) | |
51 | return mon | |
52 | end | |
53 | end | |
54 | end | |
55 | --print("no advanced monitor found. Is this an advanced computer?") | |
56 | if (term.isColor()) then | |
57 | print("using the terminal as an advanced computer") | |
58 | return(term) | |
59 | else | |
60 | error("no advanced monitor or advanced computer found. This program requires advanced interface hardward. Please install either an advanced computer or an advanced monitor",0) | |
61 | end | |
62 | end | |
63 | ||
64 | function newWidget() | |
65 | widget={} | |
66 | widget.draw=nop -- draw(widget) | |
67 | widget.onClick=nop -- onClick(widget,x,y) | |
68 | widget.onResize=nop -- onResize(widget) | |
69 | widget.hWeight=1 | |
70 | widget.vWeight=1 | |
71 | widget.width=nil | |
72 | widget.height=nil | |
73 | widget.x1=nil | |
74 | widget.y1=nil | |
75 | widget.x2=nil | |
76 | widget.y2=nil | |
77 | ||
78 | function widget.size(widget) | |
79 | if (monitor==nil) then | |
80 | monitor=getDefaultMonitor() | |
81 | end | |
82 | widget.width,widget.height=monitor.getSize() | |
83 | widget.x1=1 | |
84 | widget.y1=1 | |
85 | widget.x2=widget.width | |
86 | widget.y2=widget.height | |
87 | widget.onResize(widget) | |
88 | widget.sized=true | |
89 | end | |
90 | ||
91 | function widget.run(widget) | |
92 | if (widget==null) then | |
93 | error("invalid call to member function widget:run. Did you type widget.run instead?",2) | |
94 | end | |
95 | if (widget.sized==true) then | |
96 | else | |
97 | widget:size() | |
98 | end | |
99 | monitor.setBackgroundColor(terminalBackground) | |
100 | monitor.clear() | |
101 | widget:draw() | |
102 | widget.quit=false | |
103 | while not widget.quit do | |
104 | if (monitor==term) then | |
105 | event,side,x,y=os.pullEvent("mouse_click") | |
106 | else | |
107 | event,side,x,y=os.pullEvent("monitor_touch") | |
108 | end | |
109 | widget:processEvent(event,side,x,y) | |
110 | end | |
111 | monitor.setBackgroundColor(terminalBackground) | |
112 | monitor.clear() | |
113 | end | |
114 | ||
115 | function widget.processEvent(widget,event,side,x,y) | |
116 | if (event=="monitor_touch" or event=="mouse_click") then | |
117 | widget:onClick(x,y) | |
118 | if (redrawNeeded) then | |
119 | monitor.setBackgroundColor(terminalBackground) | |
120 | monitor.clear() | |
121 | widget:draw() | |
122 | end | |
123 | end | |
124 | end | |
125 | ||
126 | return(widget) | |
127 | end | |
128 | ||
129 | function newContainer() | |
130 | container=newWidget() | |
131 | container.children={} | |
132 | container.vSpacingWeight=.1 | |
133 | container.hSpacingWeight=.1 | |
134 | ||
135 | function container.draw(container) | |
136 | for key,child in pairs(container.children) do | |
137 | child:draw() | |
138 | end | |
139 | end | |
140 | ||
141 | function container.onClick(container,x,y) | |
142 | for key,child in pairs(container.children) do | |
143 | if x>=child.x1 and x<=child.x2 and y>=child.y1 and y<=child.y2 then | |
144 | child:onClick(x,y) | |
145 | end | |
146 | end | |
147 | end | |
148 | ||
149 | function container.resizeChildren(container) | |
150 | for key,child in pairs(container.children) do | |
151 | child:onResize() | |
152 | end | |
153 | end | |
154 | ||
155 | container.onResize=resizeChildren | |
156 | ||
157 | function container.add(container,widget) | |
158 | if (widget==nil) then | |
159 | error("attempt to add nil widget to a container. Did you perhaps type row.add when you meant row:add? Or column.add when you meant column:add?",2) | |
160 | end | |
161 | container.children[#container.children+1]=widget | |
162 | end | |
163 | ||
164 | return(container) | |
165 | end | |
166 | ||
167 | function newButton(label) | |
168 | button=newWidget() | |
169 | button.label=label | |
170 | button.backgroundColor=defaultButtonBackground | |
171 | button.foregroundColor=defaultButtonForeground | |
172 | button.labelX=nil | |
173 | button.labelY=nil | |
174 | ||
175 | function button.drawFull(button) | |
176 | button.labelX=math.floor((button.x1+button.x2-button.label:len())/2) | |
177 | button.labelY=(button.y1+button.y2)/2 | |
178 | monitor.setBackgroundColor(button.backgroundColor) | |
179 | monitor.setTextColor(button.foregroundColor) | |
180 | for x=button.x1,button.x2 do | |
181 | for y=button.y1,button.y2 do | |
182 | monitor.setCursorPos(x,y) | |
183 | monitor.write(" ") | |
184 | end | |
185 | end | |
186 | monitor.setCursorPos(button.labelX,button.labelY) | |
187 | monitor.write(button.label) | |
188 | end | |
189 | ||
190 | function button.drawOutline(button) | |
191 | button.labelX=math.floor((button.x1+button.x2-button.label:len())/2) | |
192 | button.labelY=(button.y1+button.y2)/2 | |
193 | monitor.setBackgroundColor(button.backgroundColor) | |
194 | for x=button.x1,button.x2 do | |
195 | monitor.setCursorPos(x,button.y1) | |
196 | monitor.write(" ") | |
197 | monitor.setCursorPos(x,button.y2) | |
198 | monitor.write(" ") | |
199 | end | |
200 | for y=button.y1+1,button.y2-1 do | |
201 | monitor.setCursorPos(button.x1,y) | |
202 | monitor.write(" ") | |
203 | monitor.setCursorPos(button.x2,y) | |
204 | monitor.write(" ") | |
205 | end | |
206 | monitor.setBackgroundColor(terminalBackground) | |
207 | for x=button.x1+1,button.x2-1 do | |
208 | for y=button.y1+1,button.y2-1 do | |
209 | monitor.setCursorPos(x,y) | |
210 | monitor.write(" ") | |
211 | end | |
212 | end | |
213 | monitor.setTextColor(button.foregroundColor) | |
214 | monitor.setCursorPos(button.labelX,button.labelY) | |
215 | monitor.write(button.label) | |
216 | end | |
217 | ||
218 | function button.blink(button) | |
219 | button:drawOutline() | |
220 | os.sleep(.1) | |
221 | button:draw() | |
222 | end | |
223 | ||
224 | button.draw=button.drawFull | |
225 | return(button) | |
226 | end | |
227 | ||
228 | function newRow() | |
229 | row=newContainer() | |
230 | ||
231 | function row.onResize(row) | |
232 | totalWeight=row.hSpacingWeight*(#row.children-1) | |
233 | for key,child in pairs(row.children) do | |
234 | totalWeight=totalWeight+child.hWeight | |
235 | end | |
236 | widths={} | |
237 | totalWidgetWidth=0 | |
238 | for key,child in pairs(row.children) do | |
239 | widths[key]=math.floor(row.width*child.hWeight/totalWeight) | |
240 | totalWidgetWidth=totalWidgetWidth+widths[key] | |
241 | end | |
242 | remainingWidth=row.width-totalWidgetWidth | |
243 | if (#row.children>=1) then | |
244 | separatorWidth=1 | |
245 | else | |
246 | separatorWidth=math.floor(remainingWidth/(#row.children-1)) | |
247 | end | |
248 | currentPosition=row.x1 + math.floor((row.width-totalWidgetWidth-separatorWidth*(#row.children-1))/2) | |
249 | for key,child in pairs(row.children) do | |
250 | child.x1=currentPosition | |
251 | child.y1=row.y1 | |
252 | currentPosition=currentPosition+widths[key]-1 | |
253 | child.x2=currentPosition | |
254 | child.y2=row.y2 | |
255 | currentPosition=currentPosition+separatorWidth+1 | |
256 | child.width=widths[key] | |
257 | child.height=row.height | |
258 | end | |
259 | row:resizeChildren() | |
260 | end | |
261 | ||
262 | return(row) | |
263 | end | |
264 | ||
265 | function newColumn() | |
266 | column=newContainer() | |
267 | ||
268 | function column.onResize(column) | |
269 | totalWeight=column.vSpacingWeight*(#column.children-1) | |
270 | for key,child in pairs(column.children) do | |
271 | totalWeight=totalWeight+child.vWeight | |
272 | end | |
273 | widths={} | |
274 | totalWidgetWidth=0 | |
275 | for key,child in pairs(column.children) do | |
276 | widths[key]=math.floor(column.height*child.vWeight/totalWeight) | |
277 | totalWidgetWidth=totalWidgetWidth+widths[key] | |
278 | end | |
279 | remainingWidth=column.height-totalWidgetWidth | |
280 | if (#column.children>=1) then | |
281 | separatorWidth=1 | |
282 | else | |
283 | separatorWidth=math.floor(remainingWidth/(#column.children-1)) | |
284 | end | |
285 | currentPosition=column.y1 + math.floor((column.height-totalWidgetWidth-separatorWidth*(#column.children-1))/2) | |
286 | for key,child in pairs(column.children) do | |
287 | child.x1=column.x1 | |
288 | child.y1=currentPosition | |
289 | currentPosition=currentPosition+widths[key]-1 | |
290 | child.x2=column.x2 | |
291 | child.y2=currentPosition | |
292 | child.width=column.width | |
293 | child.height=widths[key] | |
294 | currentPosition=currentPosition+separatorWidth+1 | |
295 | end | |
296 | column:resizeChildren() | |
297 | end | |
298 | ||
299 | return(column) | |
300 | end | |
301 | ||
302 | function newToggleButton(label) | |
303 | button=newButton(label) | |
304 | function button.toggle(button) | |
305 | button.state=not button.state | |
306 | button:draw() | |
307 | end | |
308 | ||
309 | function button.draw(button) | |
310 | if button.state then | |
311 | button:drawFull() | |
312 | else | |
313 | button:drawOutline() | |
314 | end | |
315 | end | |
316 | ||
317 | button.onClick=button.toggle | |
318 | button.state=false | |
319 | return(button) | |
320 | end | |
321 | ||
322 | function newLabel(text) | |
323 | label=newWidget() | |
324 | label.text=text | |
325 | label.textColor=defaultButtonForeground | |
326 | function label.draw(label) | |
327 | leftMargin=math.floor((label.width-label.text:len())/2) | |
328 | rightMargin=label.width-label.text:len()-leftMargin | |
329 | y=math.floor((label.y1+label.y2)/2) | |
330 | paddedText=string.rep(" ",leftMargin)..label.text..string.rep(" ",rightMargin) | |
331 | monitor.setTextColor(label.textColor) | |
332 | monitor.setCursorPos(label.x1,y) | |
333 | monitor.write(paddedText) | |
334 | end | |
335 | ||
336 | return(label) | |
337 | end | |
338 | ||
339 | function newSlider(min,max) | |
340 | slider=newWidget() | |
341 | slider.min=min | |
342 | slider.max=max | |
343 | slider.value=min | |
344 | slider.left=nil | |
345 | slider.foregroundColor=defaultSliderForeground | |
346 | slider.backgroundColor=defaultSliderBackground | |
347 | ||
348 | function slider.draw(slider) | |
349 | range=slider.max-slider.min+1 | |
350 | slider.left=math.floor((slider.x1+slider.x2-range)/2) | |
351 | margin=slider.left-slider.x1 | |
352 | y=math.floor((slider.y1+slider.y2)/2) | |
353 | left=slider.value-slider.min+1 | |
354 | right=slider.max-slider.value | |
355 | monitor.setCursorPos(slider.x1,y) | |
356 | monitor.setBackgroundColor(terminalBackground) | |
357 | monitor.write(string.rep(" ",margin)) | |
358 | monitor.setBackgroundColor(slider.foregroundColor) | |
359 | monitor.write(string.rep(" ",left)) | |
360 | monitor.setBackgroundColor(slider.backgroundColor) | |
361 | monitor.write(string.rep(" ",right)) | |
362 | monitor.setBackgroundColor(terminalBackground) | |
363 | monitor.setTextColor(slider.foregroundColor) | |
364 | tag=" "..tostring(slider.value*25) | |
365 | roomleft=slider.width-margin-range-tag:len() | |
366 | if (roomleft>=0) then | |
367 | monitor.write(tag..string.rep(" ",slider.width-margin-tag:len()-range)) | |
368 | end | |
369 | end | |
370 | ||
371 | function slider.onClick(slider,x,y) | |
372 | slider.value=x-slider.left+slider.min | |
373 | if (slider.value<slider.min) then | |
374 | slider.value=slider.min | |
375 | else | |
376 | if (slider.value>slider.max) then | |
377 | slider.value=slider.max | |
378 | end | |
379 | end | |
380 | slider:draw() | |
381 | end | |
382 | ||
383 | slider.adjust=slider.onClick | |
384 | return(slider) | |
385 | end | |
386 | ||
387 | function newFlagMonitor(side) -- signal display widget | |
388 | flag=newWidget() | |
389 | flag.side=side | |
390 | flag.flags=0 | |
391 | flag.sized=false | |
392 | flag.foregroundColor=defaultFlagMonitorForeground | |
393 | ||
394 | function flag.get(flag,n) | |
395 | x=bit.blshift(1,n-1) | |
396 | y=bit.band(flag.flags,x) | |
397 | z=(y~=0) | |
398 | return z | |
399 | end | |
400 | ||
401 | function flag.set(flag,n,value) | |
402 | if (value==nil) then | |
403 | value=true | |
404 | end | |
405 | if (value) then | |
406 | flag.flags=bit.bor(flag.flags,bit.blshift(1,n-1)) | |
407 | else | |
408 | flag.flags=bit.band(flag.flags,bit.bnot(bit.blshift(1,n-1))) | |
409 | end | |
410 | flag:transmit() | |
411 | end | |
412 | ||
413 | function flag.clear(flag,n) | |
414 | flag.flags=bit.band(flag.flags,bit.bnot(bit.blshift(1,n-1))) | |
415 | flag:transmit() | |
416 | end | |
417 | ||
418 | function flag.toggle(flag,n) | |
419 | flag.flags=bit.bxor(flag.flags,bit.blshift(1,n-1)) | |
420 | flag:transmit() | |
421 | end | |
422 | ||
423 | function flag.transmit(flag) | |
424 | --print("transmitting " .. flag.flags .. " to " .. flag.side) | |
425 | redstone.setBundledOutput(flag.side,flag.flags) | |
426 | flag:draw() | |
427 | end | |
428 | ||
429 | function flag.draw(flag) | |
430 | if ( not flag.sized ) then | |
431 | return | |
432 | end | |
433 | ylocal=math.floor((flag.y1+flag.y2)/2) | |
434 | xlocal=math.floor((flag.x1+flag.x2-8)/2) | |
435 | monitor.setCursorPos(xlocal,ylocal) | |
436 | for i=1,16 do | |
437 | color=bit.blshift(1,i-1) | |
438 | if (flag:get(i)) then | |
439 | monitor.setBackgroundColor(color) | |
440 | monitor.setTextColor(flag.foregroundColor) | |
441 | monitor.write(" ") | |
442 | else | |
443 | monitor.setBackgroundColor(terminalBackground) | |
444 | monitor.setTextColor(color) | |
445 | monitor.write(".") | |
446 | end | |
447 | if (i==8) then | |
448 | monitor.setCursorPos(xlocal,ylocal+1) | |
449 | end | |
450 | end | |
451 | end | |
452 | ||
453 | function flag.onResize(flag) | |
454 | flag.sized=true | |
455 | end | |
456 | ||
457 | return(flag) | |
458 | end | |
459 | ||
460 | function newChooser(choice,options) | |
461 | chooser=newButton(choice) | |
462 | chooser.options=options | |
463 | ||
464 | function chooser.choose(chooser) | |
465 | if (chooser.tab==nil) then | |
466 | chooser:formTab() | |
467 | end | |
468 | chooser.tab:run() | |
469 | redrawNeeded=true | |
470 | end | |
471 | ||
472 | function chooser.buttonOnClick(button) | |
473 | button:blink() | |
474 | button.chooser.label=button.label | |
475 | button.chooser.tab.quit=true | |
476 | end | |
477 | ||
478 | function chooser.formTab(chooser) | |
479 | chooser.tab=newRow() | |
480 | width,height=monitor.getSize() | |
481 | buttonsPerColumn=math.floor(height/4) | |
482 | numButtons=#(chooser.options) | |
483 | numColumns=math.ceil(numButtons/buttonsPerColumn) | |
484 | columnNum=1 | |
485 | rowNum=1 | |
486 | currentColumn=newColumn() | |
487 | for key,option in pairs(chooser.options) do | |
488 | if (rowNum>buttonsPerColumn) then | |
489 | rowNum=1 | |
490 | columnNum=columnNum+1 | |
491 | chooser.tab:add(currentColumn) | |
492 | currentColumn=newColumn() | |
493 | end | |
494 | choiceButton=newButton(option) | |
495 | choiceButton.vWeight=.3 | |
496 | choiceButton.chooser=chooser | |
497 | choiceButton.onClick=chooser.buttonOnClick | |
498 | currentColumn:add(choiceButton) | |
499 | rowNum=rowNum+1 | |
500 | end | |
501 | chooser.tab:add(currentColumn) | |
502 | end | |
503 | ||
504 | chooser.onClick=chooser.choose | |
505 | chooser.tab=nil | |
506 | return(chooser) | |
507 | end | |
508 | ||
509 | function newProgressBar(max) | |
510 | bar=newWidget() | |
511 | if (max==nil) then | |
512 | bar.max=1.0 | |
513 | else | |
514 | bar.max=max | |
515 | end | |
516 | bar.value=0 | |
517 | bar.foreground=defaultProgressBarForeground | |
518 | bar.background=defaultProgressBarBackground | |
519 | ||
520 | function bar.draw(bar) | |
521 | y=math.floor((bar.y1+bar.y2)/2) | |
522 | xOn=math.floor(bar.width*bar.value/bar.max) | |
523 | xOff=bar.width-xOn | |
524 | monitor.setBackgroundColor(bar.foreground) | |
525 | monitor.setCursorPos(bar.x1,y) | |
526 | monitor.write(string.rep(" ",xOn)) | |
527 | monitor.setBackgroundColor(bar.background) | |
528 | monitor.write(string.rep(" ",xOff)) | |
529 | end | |
530 | ||
531 | function bar.setValue(bar,value) | |
532 | bar.value=value | |
533 | bar:draw() | |
534 | end | |
535 | ||
536 | function bar.getValue(bar) | |
537 | return(bar.value) | |
538 | end | |
539 | ||
540 | return(bar) | |
541 | end |