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 |