[ { "title":"Code", "code":"let runBackAnimation = false;\nbackground('Stars3');\n\nconst ui = require(\"WinUI\");\nconst wm = new ui.WindowManager(ui);\nwm.DrawEveryFrame = runBackAnimation;\n\nlet y = 0;\n\nwm.LaunchApp(\"Shell\");\nwm.LaunchApp(\"prg1\");\n\n// const prgShell = require(\"Shell\");\n// const prg1 = require(\"prg1\");\n// prgShell.Run(ui, wm);\n// prg1.Run(ui, wm);\n\nfunction loop()\n{\n if (runBackAnimation)\n {\n clear();\n line(0, y, width, y);\n if (y++ >= height)\n y = 0;\n }\n \n wm.Update();\n}\n\nfunction keyPressed()\n{\n wm.keyPressed();\n}\n\nfunction keyReleased()\n{\n wm.keyReleased();\n}" }, { "title":"WinUI", "code":"// ================================================================ \n// Library of GUI controls \n// Note: These controls don't have any dependency on Window Manager \n// ================================================================ \n\nconst WINDOW_TITLEBAR_HEIGHT = 20;\nconst WINDOW_TITLE_X = 5;\nconst WINDOW_BEZEL = 4;\nconst WINDOW_TITLE_BG = \"Navy\";\nconst WINDOW_TITLE_FG = \"White\";\nconst WINDOW_OUTER_BG = \"LightBlue\";\nconst WINDOW_INNER_BG = \"White\";\nconst TEXTBOX_BG = \"White\";\nconst CONTROL_TEXT = \"Black\";\nconst CONTROL_TEXT_DISABLED = \"LightGray\";\nconst BUTTON_BG = \"WhiteSmoke\";\nconst BUTTON_ACCENT = \"DarkGray\";\nconst BUTTON_PRESSED_DELTA = 2;\n\n// Event emitter\nclass Events\n{\n constructor()\n {\n this.events = new Map();\n }\n \n Subscribe(event, fn)\n {\n let funcs = this.events.get(event);\n \n if (!funcs)\n {\n funcs = new Map();\n this.events.set(event, funcs);\n }\n \n funcs.set(fn);\n }\n \n Unsubscribe(event, fn)\n {\n let funcs = this.events.get(event);\n if (!funcs)\n return;\n \n funcs.delete(fn);\n }\n \n Emit(event, args)\n {\n let funcs = this.events.get(event);\n if (!funcs)\n return;\n\n for(let fn of funcs.keys())\n fn(...args);\n }\n}\n\nclass Control\n{\n constructor(x, y, w, h)\n {\n this.x = x;\n this.y = y;\n this.w = w;\n this.h = h;\n \n this.text = \"\";\n \n this.pressed = false;\n \n this.forecolor = CONTROL_TEXT;\n \n this.enabled = true;\n this.visible = true;\n \n this.dirty = true;\n \n this.parent = null;\n \n this.events = new Events();\n }\n \n // Public methods and properties\n\n get Text()\n {\n return this.text;\n }\n \n set Text(text)\n {\n this.text = text || \"\";\n \n this.WhenTextChanged(this);\n \n this.invalidate();\n }\n\n get X()\n {\n return this.x;\n }\n \n set X(x)\n {\n x = Number(x);\n \n if (x === this.x)\n return;\n \n this.pX = this.x;\n \n this.x = x;\n \n this.invalidate();\n \n this.WhenMove(this.pX, this.y, x, this.y);\n }\n \n get Y()\n {\n return this.y;\n }\n \n set Y(y)\n {\n y = Number(y);\n \n if (y === this.y)\n return;\n \n this.pY = this.y;\n \n this.y = y;\n \n this.invalidate();\n \n this.WhenMove(this.x, this.pY, this.x, y);\n }\n\n get Width()\n {\n return this.w;\n }\n \n set Width(width)\n {\n width = Number(width);\n \n if (width === this.w)\n return;\n \n this.pWidth = this.w;\n \n this.w = width;\n \n this.invalidate();\n \n this.WhenResize(this.pWidth, this.h, width, this.h);\n }\n \n get Height()\n {\n return this.h;\n }\n \n set Height(height)\n {\n height = Number(height);\n \n if (height === this.h)\n return;\n \n this.pHeight = this.h;\n \n this.h = height;\n \n this.invalidate();\n \n this.WhenResize(this.w, this.pHeight, this.w, height);\n }\n \n SetXY(x, y)\n {\n x = Number(x);\n y = Number(y);\n \n if (this.x === x && this.y === y)\n return;\n \n this.pX = this.x;\n this.pY = this.y;\n \n this.x = x;\n this.y = y;\n \n this.invalidate();\n \n this.WhenMove(this.pX, this.pY, x, y);\n }\n \n SetSize(width, height)\n {\n width = Number(width);\n height = Number(height);\n \n if (this.w === width && this.h === height)\n return;\n \n this.pWidth = this.w;\n this.pHeight = this.h;\n \n this.w = width;\n this.h = height;\n \n this.invalidate();\n \n this.WhenResize(this.pWidth, this.pHeight, width, height);\n }\n\n get BackColor()\n {\n return this.backcolor;\n }\n \n set BackColor(backcolor)\n {\n this.backcolor = backcolor;\n \n this.invalidate();\n }\n \n get ForeColor()\n {\n return this.forecolor;\n }\n \n set ForeColor(forecolor)\n {\n this.forecolor = forecolor;\n \n this.invalidate();\n }\n\n get Enabled()\n {\n return this.enabled;\n }\n \n set Enabled(enabled)\n {\n this.WhenEnabled(enabled);\n \n this.invalidate();\n }\n \n get Visible()\n {\n return this.visible;\n }\n \n set Visible(visible)\n {\n this.WhenVisible(visible);\n \n this.invalidate();\n }\n\n // Returns [mousex, mousey] relative to the control\n mouseXY()\n {\n let [x, y] = this.FindAbsoluteXY();\n return [floor(mouseX - x), floor(mouseY - y)];\n }\n\n // Events\n\n // Note: There are two ways to handle events in user programs:\n // - by assigning function handlers to \"On...\"\" events OR by overriding them in derived classes\n // - by subscribing to events using Subscribe method (good if more than 1 observer are interested)\n\n Subscribe(event, fn)\n {\n this.events.Subscribe(event, fn);\n }\n\n Unsubscribe(event, fn)\n {\n this.events.Subscribe(event, fn);\n }\n\n OnClick(sender)\n {\n }\n\n OnTextChanged(sender, e)\n {\n }\n\n OnMove(sender, e)\n {\n }\n\n OnResize(sender, e)\n {\n }\n \n OnVisibleChanged(sender)\n {\n }\n \n OnFocus(sender)\n {\n }\n \n OnBlur(sender)\n {\n }\n \n OnLoop()\n {\n }\n \n OnKeyPressed(sender, e)\n {\n }\n \n OnKeyReleased(sender, e)\n {\n }\n \n OnMousePressed(sender)\n {\n }\n \n OnMouseReleased(sender)\n {\n }\n\n OnTextChanged(sender, e)\n {\n }\n\n // Private members\n \n // Note: By convention all internal events are named \"When...\",\n // while public events (that can be overwritten by user) are named \"On...\"\n\n WhenMove(oldx, oldy, x, y)\n {\n let e = {pX : oldx, pY : oldy, X : x, Y : y};\n this.OnMove(this, e);\n this.events.Emit(\"OnMove\", [this, e]);\n }\n\n WhenResize(oldw, oldh, w, h)\n {\n let e = {pWidth : oldw, pHeight : oldh, Width : w, Height : h};\n this.OnResize(this, e);\n this.events.Emit(\"OnResize\", [this, e]);\n }\n\n WhenClick(sender)\n {\n this.OnClick(sender);\n this.events.Emit(\"OnClick\", [sender]);\n }\n\n WhenTextChanged(sender, e)\n {\n this.OnTextChanged(sender, e);\n this.events.Emit(\"OnTextChanged\", [sender, e]);\n }\n\n WhenEnabled(enabled)\n {\n this.enabled = enabled;\n }\n \n WhenVisible(visible)\n {\n this.visible = visible;\n\n this.OnVisibleChanged(this);\n this.events.Emit(\"OnVisibleChanged\", [this]);\n }\n \n WhenFocus()\n {\n if (!this.enabled || this.isFocused())\n return;\n \n this.focused = true;\n \n let form = this.FindForm();\n if (!form)\n return;\n\n if (form.activeControl)\n form.activeControl.WhenBlur();\n \n form.activeControl = this;\n \n this.OnFocus(this);\n this.events.Emit(\"OnFocus\", [this]);\n }\n \n WhenBlur()\n {\n if (!this.isFocused())\n return;\n \n this.focused = false;\n \n this.OnBlur(this);\n this.events.Emit(\"OnBlur\", [this]);\n }\n\n WhenLoop()\n {\n this.OnLoop();\n this.events.Emit(\"OnLoop\", [this]);\n }\n \n isFocused()\n {\n return this.focused;\n }\n\n mousePressed()\n {\n if (!this.enabled || !this.visible)\n return;\n \n this.pressed = true;\n \n if (!this.isFocused())\n this.WhenFocus();\n \n this.OnMousePressed(this);\n this.events.Emit(\"OnMousePressed\", [this]);\n }\n \n mouseReleased()\n {\n if (!this.pressed)\n return;\n \n this.pressed = false;\n\n if (this.containsMouse())\n this.WhenClick(this);\n \n this.OnMouseReleased(this);\n this.events.Emit(\"OnMouseReleased\", [this]);\n }\n\n mouseDoubleClicked()\n {\n }\n\n mouseDragged()\n {\n }\n\n keyPressed()\n {\n let e = { key : key, keyCode : keyCode };\n\n this.OnKeyPressed(this, e);\n this.events.Emit(\"OnKeyPressed\", [this, e]);\n }\n \n keyReleased()\n {\n let e = { key : key, keyCode : keyCode };\n\n this.OnKeyReleased(this, e);\n this.events.Emit(\"OnKeyReleased\", [this, e]);\n }\n\n containsMouse()\n {\n let [x, y] = this.FindAbsoluteXY();\n return collisionPointRect(mouseX, mouseY, x, y, this.w, this.h);\n }\n\n draw()\n {\n if (this.visible)\n this.owndraw();\n \n this.setDirty(false);\n }\n \n owndraw()\n {\n }\n \n invalidate()\n {\n this.setDirty(true);\n }\n \n FindAbsoluteXY()\n {\n let absX = this.x;\n let absY = this.y;\n \n if (this.parent)\n {\n let coord = this.parent.FindAbsoluteXY();\n absX += coord[0];\n absY += coord[1];\n }\n \n return [absX, absY];\n }\n \n FindForm()\n {\n let p = this;\n \n while(true)\n {\n if (p instanceof Form)\n return p;\n \n if (p.parent === null)\n return null;\n \n p = p.parent;\n }\n \n return p; \n }\n\n FitCaption(txt, w)\n {\n if (textWidth(txt) <= w)\n return txt;\n \n while(textWidth(txt + \"...\") > w)\n {\n txt = txt.slice(0, txt.length - 2);\n }\n \n return txt + \"...\";\n }\n\n isDirty()\n {\n return this.dirty;\n }\n \n setDirty(dirty)\n {\n this.dirty = dirty;\n }\n}\n\nclass Button extends Control\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n \n this.BackColor = BUTTON_BG;\n this.ForeColor = CONTROL_TEXT;\n }\n\n // Public\n \n // Private, protected, overrides, etc.\n\n mousePressed()\n {\n super.mousePressed();\n\n this.invalidate();\n }\n \n mouseReleased()\n {\n if (!this.pressed)\n return;\n\n super.mouseReleased();\n \n this.invalidate();\n }\n\n owndraw()\n {\n push();\n \n let [x, y] = this.FindAbsoluteXY();\n\n let pressedDelta = this.pressed ? BUTTON_PRESSED_DELTA / 2 : 0;\n\n noStroke();\n \n fill(BUTTON_ACCENT);\n rect(x + BUTTON_PRESSED_DELTA, y + BUTTON_PRESSED_DELTA, this.w - BUTTON_PRESSED_DELTA, this.h - BUTTON_PRESSED_DELTA);\n\n stroke(this.Enabled ? this.ForeColor : CONTROL_TEXT_DISABLED);\n fill(this.BackColor);\n rect(x + pressedDelta, y + pressedDelta, this.w - BUTTON_PRESSED_DELTA, this.h - BUTTON_PRESSED_DELTA);\n\n textAlign(CENTER, CENTER);\n noStroke();\n fill(this.Enabled ? this.ForeColor : CONTROL_TEXT_DISABLED);\n text(this.FitCaption(this.text, this.w), x + this.w / 2 + pressedDelta, y + this.h / 2 + pressedDelta);\n \n pop();\n }\n}\n\nclass TextBox extends Control\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n \n this.editMode = false;\n this.positionFromEnd = 0;\n \n this.forecolor = CONTROL_TEXT;\n this.backcolor = TEXTBOX_BG;\n }\n\n // Public\n\n get ReadOnly()\n {\n return this.readonly;\n }\n\n set ReadOnly(readonly)\n {\n this.readonly = readonly;\n }\n\n // Events\n\n // Private, protected, overrides, etc.\n\n keyPressed()\n {\n super.keyPressed();\n \n switch(keyCode)\n {\n case ENTER:\n this.WhenBlur();\n break;\n \n case LEFT_ARROW:\n if (this.positionFromEnd < this.text.length)\n this.positionFromEnd++;\n break;\n \n case RIGHT_ARROW:\n if (this.positionFromEnd > 0)\n this.positionFromEnd--;\n break;\n\n case 36: //HOME:\n this.positionFromEnd = this.text.length;\n break;\n\n case 35: //END:\n this.positionFromEnd = 0;\n break;\n \n case ESCAPE:\n if (this.text !== this.textBackup)\n this.Text = this.textBackup;\n\n this.WhenBlur();\n break;\n \n case BACKSPACE:\n this.Text = this.deleteCharAtCursor(true);\n break;\n \n case DELETE:\n this.Text = this.deleteCharAtCursor(false);\n break;\n \n default:\n if (key.length === 1 && textWidth(this.text + key) < this.w)\n this.Text = this.insertCharAtCursor(key);\n }\n \n this.invalidate();\n }\n \n WhenFocus()\n {\n super.WhenFocus();\n \n this.editMode = true;\n this.positionFromEnd = 0;\n \n this.textBackup = this.text;\n \n this.invalidate();\n }\n \n WhenBlur()\n {\n super.WhenBlur();\n \n this.editMode = false;\n \n this.invalidate();\n }\n\n deleteCharAtCursor(leftOrRight)\n {\n if (this.readonly)\n return this.text;\n\n let n = this.text.length;\n let p = n - this.positionFromEnd;\n \n let s1 = this.text.slice(0, p);\n let s2 = this.text.slice(p);\n\n if (leftOrRight)\n {\n s1 = s1.slice(0, s1.length - 1);\n }\n\n else\n {\n s2 = s2.slice(1);\n this.positionFromEnd--;\n }\n \n return s1 + s2;\n }\n\n insertCharAtCursor(chr)\n {\n if (this.readonly)\n return this.text;\n\n let n = this.text.length;\n let p = n - this.positionFromEnd;\n \n let s1 = this.text.slice(0, p);\n let s2 = this.text.slice(p);\n \n return s1 + chr + s2;\n }\n\n drawEditCursor(x, y)\n {\n if (!this.editMode)\n return;\n\n let n = this.text.length;\n let p = n - this.positionFromEnd;\n \n let s1 = this.text.slice(0, p);\n let w = textWidth(s1);\n let h = 0.75 * this.h;\n\n push();\n stroke(\"Black\");\n strokeWeight(1);\n line(x + w, y - h / 2, x + w, y + h / 2);\n pop();\n }\n\n owndraw()\n {\n push();\n \n let [x, y] = this.FindAbsoluteXY();\n\n let color = this.enabled ? this.forecolor : CONTROL_TEXT_DISABLED;\n\n stroke(color);\n fill(this.backcolor);\n rect(x, y, this.w, this.h);\n\n textAlign(LEFT, CENTER);\n noStroke();\n fill(color);\n \n text(this.text, x + 2, y + this.h / 2);\n this.drawEditCursor(x + 2, y + this.h / 2);\n \n pop();\n }\n}\n\nclass ContainerControl extends Control\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n \n this.controls = [];\n }\n\n // Public\n\n AddControl(ctrl)\n {\n this.controls.push(ctrl);\n ctrl.parent = this;\n\n this.invalidate();\n \n this.WhenControlAdded(ctrl);\n }\n \n RemoveControls()\n {\n this.controls.length = 0;\n this.invalidate();\n }\n\n get Controls()\n {\n return this.controls;\n }\n \n // Private, protected, overrides, etc.\n\n WhenControlAdded(ctrl)\n {\n if (!this.Enabled)\n ctrl.Enabled = false;\n \n if (!this.Visible)\n ctrl.Visible = false;\n }\n\n WhenEnabled(enabled)\n {\n super.WhenEnabled(enabled);\n\n for(let ctrl of this.controls)\n {\n ctrl.Enabled = enabled;\n }\n }\n\n WhenLoop()\n {\n super.WhenLoop();\n\n for(let ctrl of this.controls)\n {\n ctrl.WhenLoop();\n }\n }\n\n isFocused()\n {\n if (this.focused)\n return true;\n \n for(let ctrl of this.controls)\n {\n if (ctrl.isFocused())\n return true;\n }\n \n return false;\n }\n\n mousePressed()\n {\n super.mousePressed();\n \n for(let ctrl of this.controls)\n {\n if (ctrl.containsMouse())\n ctrl.mousePressed();\n }\n }\n \n mouseReleased()\n {\n super.mouseReleased();\n \n for(let ctrl of this.controls)\n {\n ctrl.mouseReleased();\n }\n }\n \n mouseDragged()\n {\n for(let ctrl of this.controls)\n {\n ctrl.mouseDragged();\n }\n }\n\n draw()\n {\n if (!this.visible)\n {\n this.setDirty(false);\n return;\n }\n \n super.draw();\n \n drawingContext.save();\n drawingContext.beginPath();\n drawingContext.rect(...this.FindAbsoluteXY(), this.w, this.h);\n drawingContext.clip();\n\n for(let ctrl of this.controls)\n ctrl.draw();\n\n drawingContext.restore();\n }\n\n isDirty()\n {\n if (this.dirty === true)\n return true;\n \n for(let ctrl of this.controls)\n {\n if (ctrl.isDirty())\n return true;\n }\n \n return false;\n }\n \n setDirty(dirty)\n {\n this.dirty = dirty;\n \n for(let ctrl of this.controls)\n {\n ctrl.setDirty(dirty);\n }\n }\n}\n\nclass Panel extends ContainerControl\n{\n // Public \n \n // Private, protected, overrides, etc.\n \n owndraw()\n {\n let [x, y] = this.FindAbsoluteXY();\n \n push();\n \n if (this.backcolor)\n fill(this.backcolor);\n \n else\n noFill();\n \n if (this.forecolor) \n stroke(this.forecolor);\n \n else\n noStroke();\n \n rect(x, y, this.w, this.h);\n pop();\n }\n}\n\nclass Label extends Control\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n \n this.textalign = [LEFT, TOP];\n }\n \n // Public\n \n SetTextAlign(horiz, vert)\n {\n this.textalign = [horiz, vert];\n }\n \n SetTextStyle(textstyle)\n {\n this.textstyle = textstyle;\n }\n \n // Private, protected, overrides, etc.\n \n owndraw()\n {\n push();\n \n let [x, y] = this.FindAbsoluteXY();\n\n textAlign(...this.textalign);\n textStyle(this.textstyle);\n noStroke();\n fill(this.enabled ? this.forecolor : CONTROL_TEXT_DISABLED);\n text(this.text, x, y, this.w, this.h);\n \n pop();\n }\n}\n\nclass CheckBox extends Control\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n \n this.checked = false;\n this.rectSize = 14;\n \n // Set activeAll to false if you want only the square to be clickable\n this.activeAll = true;\n }\n \n // Public\n \n get Checked()\n {\n return this.checked;\n }\n \n set Checked(checked)\n {\n if (checked === this.checked)\n return;\n \n this.checked = checked;\n this.invalidate();\n \n this.WhenCheckedChanged();\n }\n \n // Events\n \n OnCheckedChanged(sender, e)\n {\n }\n \n // Private, protected, overrides, etc.\n\n mousePressed()\n {\n super.mousePressed();\n \n if (!this.pressed)\n return;\n\n // Check for collisions just inside the rect. For the entire control, just remove 'if' line\n if (this.activeAll ||collisionPointRect(...this.mouseXY(), 0, 0, this.rectSize, this.rectSize))\n {\n this.Checked = !this.Checked;\n }\n }\n\n WhenCheckedChanged()\n {\n this.OnCheckedChanged(this);\n this.events.Emit(\"OnCheckedChanged\", [this]);\n }\n\n owndraw()\n {\n push();\n \n let [x, y] = this.FindAbsoluteXY();\n\n let color = this.enabled ? this.forecolor : CONTROL_TEXT_DISABLED;\n\n stroke(color);\n rect(x, y, this.rectSize, this.rectSize);\n\n textAlign(LEFT, TOP);\n noStroke();\n fill(color);\n \n if (this.checked)\n {\n textStyle(BOLD);\n text(\"x\", x + 4, y + 2);\n \n // Alternative check...\n // rect(x + 4, y + 4, this.rectSize / 2, this.rectSize / 2);\n }\n \n textStyle(NORMAL);\n text(this.text, x + 20, y + 3, this.w, this.h);\n\n pop();\n }\n}\n\nclass RadioButton extends Control\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n \n this.checked = false;\n this.circleR = 7;\n \n // Set activeAll to false if you want only the circle to be clickable\n this.activeAll = true;\n }\n \n // Public\n \n get Checked()\n {\n return this.checked;\n }\n \n set Checked(checked)\n {\n if (checked === this.checked)\n return;\n \n this.clearAll(this);\n this.checked = checked;\n this.invalidate();\n\n this.WhenCheckedChanged();\n }\n \n // Events\n \n OnCheckedChanged(sender, e)\n {\n }\n \n // Private, protected, overrides, etc.\n\n mousePressed()\n {\n super.mousePressed();\n\n if (!this.pressed)\n return;\n\n // Check for collisions just inside the rect. For the entire control, just remove 'if' line\n if (this.activeAll || collisionPointCircle(...this.mouseXY(), this.circleR, this.circleR, this.circleR))\n {\n this.Checked = true;\n }\n }\n\n clearAll(radioExcept)\n {\n if (!this.parent)\n return;\n \n for(let ctrl of this.parent.Controls)\n {\n if (ctrl === radioExcept)\n continue;\n \n if (ctrl instanceof RadioButton)\n ctrl.Checked = false;\n }\n }\n\n WhenCheckedChanged()\n {\n this.OnCheckedChanged(this);\n this.events.Emit(\"OnCheckedChanged\", [this]);\n }\n\n owndraw()\n {\n push();\n \n let [x, y] = this.FindAbsoluteXY();\n\n let color = this.enabled ? this.forecolor : CONTROL_TEXT_DISABLED;\n\n stroke(color);\n circle(x + this.circleR, y + this.circleR, this.circleR);\n\n textAlign(LEFT, TOP);\n noStroke();\n fill(color);\n \n if (this.checked)\n {\n circle(x + this.circleR, y + this.circleR, this.circleR / 2);\n }\n \n textStyle(NORMAL);\n text(this.text, x + 20, y + 3, this.w, this.h);\n\n pop();\n }\n \n}\n\nclass FlatButton extends Button\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n }\n \n // Private, protected, overrides, etc.\n \n owndraw()\n {\n push();\n \n let [x, y] = this.FindAbsoluteXY();\n\n let pressedDelta = this.pressed ? BUTTON_PRESSED_DELTA / 2 : 0;\n\n textAlign(CENTER, CENTER);\n noStroke();\n fill(this.enabled ? this.forecolor : CONTROL_TEXT_DISABLED);\n text(this.text, x + this.w / 2 + pressedDelta, y + this.h / 2 + pressedDelta);\n \n pop();\n }\n}\n\nclass Slider extends Control\n{\n constructor(x, y, w, h = 10)\n {\n super(x, y, w, h);\n\n this.BackColor = BUTTON_BG;\n this.ForeColor = CONTROL_TEXT;\n\n this.minimum = 0;\n this.maximum = 100;\n this.value = 50;\n\n this.knobRadius = 5;\n this.sliderHeight = 3;\n }\n\n // Public\n\n get Minimum()\n {\n return this.minimum;\n }\n\n set Minimum(minimum)\n {\n this.minimum = minimum;\n }\n\n get Maximum()\n {\n return this.maximum;\n }\n\n set Maximum(maximum)\n {\n this.maximum = maximum;\n }\n\n get Value()\n {\n return this.value;\n }\n\n set Value(value)\n {\n value = constrain(value, this.minimum, this.maximum);\n \n if (value === this.value)\n return;\n \n this.value = value;\n\n this.invalidate();\n\n this.WhenValueChanged(this);\n }\n\n // Events\n\n OnValueChanged(sender, e)\n {\n }\n\n // Private, protected, overrides, etc.\n\n WhenValueChanged(sender, e)\n {\n this.OnValueChanged(sender, e);\n this.events.Emit(\"OnValueChanged\", [sender, e]);\n }\n\n mousePressed()\n {\n super.mousePressed();\n\n let [mx, my] = this.mouseXY();\n let [kx, ky] = this.getKnobXY();\n\n this.knobGrabbed = collisionPointCircle(mx, my, kx, ky, this.knobRadius);\n\n this.invalidate();\n }\n\n mouseReleased()\n {\n super.mouseReleased();\n\n this.knobGrabbed = false;\n\n this.invalidate();\n }\n\n mouseDragged()\n {\n super.mouseDragged();\n\n if (!this.knobGrabbed)\n return;\n\n let [mx, my] = this.mouseXY();\n\n let p = mx / this.w;\n\n this.Value = this.minimum + p * (this.maximum - this.minimum);\n }\n\n getKnobXY()\n {\n let p = (this.value - this.minimum) / (this.maximum - this.minimum);\n let d = p * this.w;\n\n return [d, this.knobRadius];\n }\n\n owndraw()\n {\n push();\n\n let [x, y] = this.FindAbsoluteXY();\n\n let color = this.enabled ? this.forecolor : CONTROL_TEXT_DISABLED;\n\n stroke(color);\n fill(this.backcolor);\n rect(x, y + this.knobRadius - this.sliderHeight / 2, this.w, this.sliderHeight);\n\n let [kx, ky] = this.getKnobXY();\n\n fill(this.knobGrabbed ? this.forecolor : this.backcolor);\n circle(x + kx, y + ky, this.knobRadius);\n \n if (this.knobGrabbed)\n {\n textAlign(CENTER, CENTER);\n noStroke();\n fill(this.forecolor);\n text(this.value.toFixed(2), x + kx, y + ky - 12);\n }\n\n pop();\n }\n\n}\n\nclass Form extends Panel\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n this.BackColor = WINDOW_INNER_BG;\n }\n \n // Public\n \n // Private, internal, overrides, etc...\n \n}\n\nclass Canvas extends Control\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n \n this.gfx = createGraphics(w, h);\n \n this.gfx.keyIsDown = (k) => {\n return this.focused ? keyIsDown(k) : false;\n };\n \n this.Subscribe(\"OnResize\", (sender, e) => {\n // If the resize event comes from the inner Gfx that is changing size...\n if (this.gfxResizing)\n return;\n \n // If the resize event comes from the outer form that invokes canvas.SetSize...\n this.gfx.resizeCanvas(e.Width, e.Height);\n \n if (this.sketch && this.sketch.resize)\n this.sketch.resize(this.gfx);\n });\n }\n\n // Public\n\n get Gfx()\n {\n return this.gfx;\n }\n \n get Sketch()\n {\n return this.sketch;\n }\n \n // Sketch is an object with 2 optional methods:\n // - setup\n // - loop\n set Sketch(sketch)\n {\n this.sketch = sketch;\n \n if (this.sketch.setup)\n {\n this.sketch.setup(this.gfx);\n \n this.gfxResizing = true;\n this.SetSize(this.gfx.width, this.gfx.height);\n this.gfxResizing = false;\n\n this.invalidate();\n }\n }\n\n WhenLoop()\n {\n super.WhenLoop();\n \n if (this.sketch && this.sketch.loop)\n {\n this.UpdateMouseKeyboard();\n \n this.sketch.loop(this.gfx);\n this.invalidate();\n }\n }\n\n mousePressed()\n {\n super.mousePressed();\n \n if (this.sketch && this.sketch.mousePressed)\n {\n this.sketch.mousePressed(this.gfx);\n }\n }\n\n mouseReleased()\n {\n if (!this.pressed)\n return;\n\n super.mouseReleased();\n\n if (this.sketch && this.sketch.mouseReleased)\n {\n this.sketch.mouseReleased(this.gfx);\n }\n }\n\n keyPressed()\n {\n super.keyPressed();\n \n if (this.sketch && this.sketch.keyPressed)\n {\n this.sketch.keyPressed(this.gfx);\n }\n }\n \n keyReleased()\n {\n super.keyReleased();\n \n if (this.sketch && this.sketch.keyReleased)\n {\n this.sketch.keyReleased(this.gfx);\n }\n }\n\n // Private, protected, overrides, etc.\n\n UpdateMouseKeyboard()\n {\n let [mx, my] = this.mouseXY();\n \n this.gfx.mouseX = mx;\n this.gfx.mouseY = my;\n \n if (this.focused)\n {\n this.gfx.keyIsPressed = keyIsPressed;\n this.gfx.key = key;\n this.gfx.keyCode = keyCode;\n }\n else\n {\n this.gfx.keyIsPressed = false;\n }\n }\n\n owndraw()\n {\n let [x, y] = this.FindAbsoluteXY();\n \n image(this.gfx, x, y);\n \n this.invalidate();\n }\n}\n\n// This form allows you to run mini sketches in a window environment\n// Example:\n// let frm = new wm.SketchForm(10, 10, 320, 200);\n// frm.Sketch = require(\"Program\");\n// wm.AddForm(frm);\nclass SketchForm extends Form\n{\n constructor(x, y, w, h)\n {\n super(x, y, w, h);\n \n this.canvas = new Canvas(0, 0, w, h);\n this.AddControl(this.canvas);\n \n this.Subscribe(\"OnResize\", (sender, e) => {\n // If canvas is changing size and forces the form to set a new size... then exit\n if (this.canvasResizing)\n return;\n \n // otherwise change canvas size because the resize is requested on form externally\n this.canvas.SetSize(e.Width, e.Height);\n });\n \n this.canvas.Subscribe(\"OnResize\", (sender, e) => {\n this.canvasResizing = true;\n this.SetSize(e.Width, e.Height);\n this.canvasResizing = false;\n });\n }\n \n // Public\n \n get Sketch()\n {\n return this.canvas.Sketch;\n }\n \n set Sketch(sketch)\n {\n this.canvas.Sketch = sketch;\n \n if (this.canvas.Gfx.Text)\n this.Text = this.canvas.Gfx.Text;\n }\n \n // Private, protected, overrides, etc.\n\n WhenFocus()\n {\n super.WhenFocus();\n \n this.canvas.WhenFocus();\n }\n\n}\n \n// ============================================================== \n// Window Manager Library \n// Window Manager is creating windows from Forms and manages them \n// ============================================================== \n\nclass TitlePanel extends Panel\n{\n constructor(x, y, w, h)\n {\n h = WINDOW_TITLEBAR_HEIGHT;\n \n super(x, y, w, h);\n \n this.backcolor = WINDOW_TITLE_BG;\n\n this.titleLabel = new Label(WINDOW_TITLE_X, 0, w, h);\n this.titleLabel.SetTextAlign(LEFT, CENTER);\n this.titleLabel.ForeColor = WINDOW_TITLE_FG;\n this.titleLabel.Text = \"Window\";\n this.AddControl(this.titleLabel);\n\n this.minButton = new FlatButton(w - 3 * h, 0, h, h);\n this.minButton.Text = \"-\";\n this.minButton.ForeColor = WINDOW_TITLE_FG;\n this.AddControl(this.minButton);\n\n this.maxButton = new FlatButton(w - 2 * h, 0, h, h);\n this.maxButton.Text = \"=\";\n this.maxButton.ForeColor = WINDOW_TITLE_FG;\n this.maxButton.OnVisibleChanged = () => {\n this.minButton.x = this.maxButton.Visible ? w - 3 * h : w - 2 * h;\n };\n this.AddControl(this.maxButton);\n\n this.closeButton = new FlatButton(w - h, 0, h, h);\n this.closeButton.Text = \"x\";\n this.closeButton.ForeColor = WINDOW_TITLE_FG;\n this.AddControl(this.closeButton);\n }\n \n WhenResize(oldw, oldh, w, h)\n {\n super.WhenResize(oldw, oldh, w, h);\n \n this.titleLabel.Width = w;\n this.minButton.x = w - 3 * this.h;\n this.maxButton.x = w - 2 * this.h;\n this.closeButton.x = w - this.h;\n }\n}\n\nclass BaseWindow extends ContainerControl\n{\n constructor(form)\n {\n super(form.x, form.y, form.w, form.h);\n \n this.form = form;\n this.form.x = 0;\n this.form.y = 0;\n this.AddControl(this.form);\n \n this.form.parent = this;\n \n this.owner = null;\n }\n \n // Public\n \n get Form()\n {\n return this.form;\n }\n\n get Owner()\n {\n return this.owner;\n }\n \n set Owner(owner)\n {\n this.owner = owner;\n }\n\n get FixedWindow()\n {\n return this.fixedwindow;\n }\n\n set FixedWindow(fixedwindow)\n {\n this.fixedwindow = fixedwindow;\n }\n\n get AutoClose()\n {\n return this.autoclose;\n }\n \n set AutoClose(autoclose)\n {\n this.autoclose = autoclose;\n }\n \n // Returns the edge or corner on which is the mouse cursor\n GetMouseHit()\n {\n let margin = 4;\n \n if (collisionPointRect(mouseX, mouseY, this.x + margin, this.y - margin, this.w - 2 * margin, margin * 2))\n return \"top\";\n \n if (collisionPointRect(mouseX, mouseY, this.x + margin, this.y + this.h - margin, this.w - 2 * margin, margin * 2))\n return \"bottom\";\n\n if (collisionPointRect(mouseX, mouseY, this.x - margin, this.y + margin, margin * 2, this.h - 2 * margin))\n return \"left\";\n\n if (collisionPointRect(mouseX, mouseY, this.x + this.w - margin, this.y + margin, margin * 2, this.h - 2 * margin))\n return \"right\";\n\n if (collisionPointRect(mouseX, mouseY, this.x - margin, this.y - margin, margin * 2, margin * 2))\n return \"lefttop\";\n\n if (collisionPointRect(mouseX, mouseY, this.x + this.w - margin, this.y - margin, margin * 2, margin * 2))\n return \"righttop\";\n\n if (collisionPointRect(mouseX, mouseY, this.x - margin, this.y + this.h - margin, margin * 2, margin * 2))\n return \"leftbottom\";\n\n if (collisionPointRect(mouseX, mouseY, this.x + this.w - margin, this.y + this.h - margin, margin * 2, margin * 2))\n return \"rightbottom\";\n \n return \"\";\n }\n\n Close()\n {\n let e = { Cancel : false };\n this.OnClose(this, e);\n \n if (!e.Cancel)\n this.WindowClose(this); \n }\n \n // Cancelable event\n OnClose(sender, e)\n {\n }\n \n OnClosed(sender,e )\n {\n }\n\n // Private, protected, overrides, etc.\n\n // Can be intercepted by Window Manager to paint other windows if needed\n WindowAfterDraw(win)\n {\n }\n\n // Will be defined by the WM\n WindowMinimize()\n {\n }\n \n // Will be defined by the WM\n WindowMaximizeRestore()\n {\n }\n\n // Can be intercepted by Window Manager to close the window\n WindowClose(win, e)\n {\n }\n \n // Can be intercepted by Window Manager to refresh other windows\n WindowResized(oldw, oldh, w, h)\n {\n }\n \n // Can be intercepted by Window Manager to refresh other windows\n WindowVisibleChanged()\n {\n }\n \n // Can be intercepted by Window Manager to refresh other windows\n WindowMoved(oldx, oldy, x, y)\n {\n }\n\n WindowClosed()\n {\n this.OnClosed(this);\n }\n\n WhenResize(oldw, oldh, w, h)\n {\n this.inResize = true;\n \n super.WhenResize(oldw, oldh, w, h);\n \n this.inResize = false;\n }\n\n WhenFocus()\n {\n super.WhenFocus();\n \n this.form.WhenFocus();\n }\n \n WhenBlur()\n {\n super.WhenBlur();\n \n if (this.form.activeControl)\n this.form.activeControl.WhenBlur();\n }\n \n containsMouse()\n {\n return collisionPointRect(mouseX, mouseY, this.x, this.y, this.w, this.h);\n }\n\n draw()\n {\n super.draw();\n\n this.WindowAfterDraw(this);\n }\n}\n\nclass FramelessWindow extends BaseWindow\n{\n constructor(form)\n {\n super(form);\n \n this.form.Subscribe(\"OnResize\", (sender, e) => {\n if (!this.inResize)\n {\n this.Width = e.Width;\n this.Height = e.Height;\n }\n });\n\n this.Subscribe(\"OnResize\", (sender, e) => {\n if (e.pWidth !== e.Width)\n {\n this.form.Width = e.Width;\n }\n \n if (e.pHeight !== e.Height)\n this.form.Height = e.Height;\n \n this.WindowResized(e.pWidth, e.pHeight, e.Width, e.Height);\n });\n \n this.Subscribe(\"OnMove\", (sender, e) => {\n this.WindowMoved(e.pX, e.pY, e.X, e.Y);\n });\n \n this.Subscribe(\"OnVisibleChanged\", () => {\n this.WindowVisibleChanged();\n });\n\n }\n}\n\nclass Window extends BaseWindow\n{\n constructor(form)\n {\n let w = form.w;\n let h = form.h;\n \n form.w = w + 2 * WINDOW_BEZEL;\n form.h = h + WINDOW_TITLEBAR_HEIGHT + 3 * WINDOW_BEZEL;\n \n super(form);\n\n this.form = form;\n this.form.x = WINDOW_BEZEL;\n this.form.y = 2 * WINDOW_BEZEL + WINDOW_TITLEBAR_HEIGHT;\n this.form.w = w;\n this.form.h = h;\n this.form.OnTextChanged = (sender) => { this.Text = sender.Text; };\n\n // If the \"Form\" object is asked to resize (e.g. form.Width = ...)\n this.form.Subscribe(\"OnResize\", (sender, e) => {\n // If the Window initiated the resize, don't ask again the window to resize\n if (!this.inResize)\n {\n this.Width = e.Width + 2 * WINDOW_BEZEL;\n this.Height = e.Height + WINDOW_TITLEBAR_HEIGHT + 3 * WINDOW_BEZEL;\n }\n });\n\n // If the \"Window\" object is asked to resize (e.g. win.Width = ...)\n this.Subscribe(\"OnResize\", (sender, e) => {\n if (e.pWidth !== e.Width)\n {\n this.form.Width = e.Width - 2 * WINDOW_BEZEL;\n this.titleBar.Width = e.Width - 2 * WINDOW_BEZEL;\n }\n \n if (e.pHeight !== e.Height)\n this.form.Height = e.Height - WINDOW_TITLEBAR_HEIGHT - 3 * WINDOW_BEZEL;\n \n this.WindowResized(e.pWidth, e.pHeight, e.Width, e.Height);\n });\n \n this.Subscribe(\"OnMove\", (sender, e) => {\n this.WindowMoved(e.pX, e.pY, e.X, e.Y);\n });\n \n this.Subscribe(\"OnVisibleChanged\", () => {\n this.WindowVisibleChanged();\n });\n\n this.titleBar = new TitlePanel(WINDOW_BEZEL, WINDOW_BEZEL, w, WINDOW_TITLEBAR_HEIGHT);\n this.titleBar.titleLabel.Text = this.form.Text;\n this.titleBar.minButton.OnClick = () => { this.WindowMinimize(this); };\n this.titleBar.maxButton.OnClick = () => { this.WindowMaximizeRestore(this); };\n this.titleBar.closeButton.OnClick = () => { this.Close(this); };\n this.AddControl(this.titleBar);\n }\n\n // Private, protected, overrides, etc.\n\n get MinimizeButton()\n {\n return this.titleBar.minButton.Visible;\n }\n \n set MinimizeButton(minimizebutton)\n {\n this.titleBar.minButton.Visible = minimizebutton;\n }\n \n get MaximizeButton()\n {\n return this.titleBar.maxButton.Visible;\n }\n \n set MaximizeButton(maximizebutton)\n {\n this.titleBar.maxButton.Visible = maximizebutton;\n }\n\n get CloseButton()\n {\n return this.titleBar.closeButton.Visible;\n }\n\n set CloseButton(closebutton)\n {\n this.titleBar.closeButton.Visible = closebutton;\n }\n \n get Sizable()\n {\n return this.MinimizeButton && this.MaximizeButton;\n }\n \n set Sizable(sizable)\n {\n this.MinimizeButton = sizable;\n this.MaximizeButton = sizable;\n }\n\n get Text()\n {\n return this.titleBar.titleLabel.Text || this.text;\n }\n \n set Text(text)\n {\n this.titleBar.titleLabel.Text = text;\n this.text = text;\n }\n\n keyPressed()\n {\n super.keyPressed();\n \n if (this.form.activeControl)\n this.form.activeControl.keyPressed();\n }\n \n keyReleased()\n {\n super.keyPressed();\n \n if (this.form.activeControl)\n this.form.activeControl.keyReleased();\n }\n\n WhenFocus()\n {\n super.WhenFocus();\n this.titleBar.titleLabel.SetTextStyle(BOLD);\n }\n \n WhenBlur()\n {\n super.WhenBlur();\n this.titleBar.titleLabel.SetTextStyle(NORMAL);\n }\n\n GetMouseHit()\n {\n if (collisionPointRect(mouseX, mouseY, this.x + WINDOW_BEZEL, this.y + WINDOW_BEZEL, this.w - WINDOW_TITLEBAR_HEIGHT, WINDOW_TITLEBAR_HEIGHT))\n return \"title\";\n\n return super.GetMouseHit();\n }\n\n owndraw()\n {\n push();\n \n noStroke();\n fill(WINDOW_OUTER_BG);\n rect(this.x, this.y, this.w, this.h);\n pop();\n }\n}\n\nclass Dialogs\n{\n constructor(wm)\n {\n this.wm = wm;\n }\n \n // Shows a message box window in the middle of the screen\n // If owner is specified, the box is made modal to that window\n MessageBox(message, owner, callBack)\n {\n let x = 250;\n let y = 200;\n let w = 300; \n let h = 120;\n\n let form = new Form(x, y, w, h);\n form.text = \"Message\";\n \n let lbl = new Label(20, 30, w - 40, 30);\n lbl.SetTextAlign(CENTER, CENTER);\n lbl.Text = message;\n form.AddControl(lbl);\n \n let btn = new Button(w / 2 - 30, h - 35, 60, 20);\n btn.Text = \"OK\";\n form.AddControl(btn);\n\n let win = new Window(form);\n \n win.MinimizeButton = false;\n win.MaximizeButton = false;\n \n btn.OnClick = (btn) => {win.Close();};\n\n win.OnClosed = (win) => {\n if (callBack)\n callBack(win);\n };\n\n win.OnKeyPressed = (sender, e) => {\n if (e.keyCode === ESCAPE || e.keyCode === 32 || e.keyCode === ENTER)\n win.Close();\n };\n\n if (owner && (owner instanceof Form))\n owner = owner.parent;\n\n this.wm.AddWindow(win, owner);\n }\n}\n\n// Window Manager\nclass WindowManager\n{\n constructor(ui)\n {\n // used by LaunchApp and can be used in future also by WindowManager if Controls will be used in a separate file\n this.ui = ui;\n \n this.windows = [];\n \n this.wasMousePressed = false;\n this.grabbedWindow = null;\n this.grabbedWindowDX = 0;\n this.grabbedWindowDY = 0;\n \n this.events = new Events();\n this.dialogs = new Dialogs(this);\n }\n\n // If .DrawEveryFrame = true then the Window manager\n // will draw all windows every frame (good if you have background animations)\n get DrawEveryFrame()\n {\n return this.draweveryframe;\n }\n \n set DrawEveryFrame(draweveryframe)\n {\n this.draweveryframe = draweveryframe;\n }\n\n // Need to be called from the main Loop to make Window Manager work\n Update()\n {\n this.handleMouse();\n \n for(let win of this.windows)\n {\n win.WhenLoop();\n \n if ( this.draweveryframe || (win.Visible && win.isDirty()) )\n win.draw();\n }\n }\n\n // Add a form to the collection\n // Forms are wrapped in windows before addition\n AddForm(form, owner)\n {\n let win = new Window(form);\n \n // If form is specified as another form...\n if (owner && (owner instanceof Form))\n owner = owner.parent;\n \n this.AddWindow(win, owner);\n \n return win;\n }\n\n // Add a window to the collection\n // If owner is specified, then win becomes a modal of owner\n AddWindow(win, owner)\n {\n let oldwin = this.windows.peek();\n if (oldwin)\n {\n oldwin.dirty = true;\n oldwin.WhenBlur();\n }\n\n this.windows.push(win);\n\n win.owner = owner;\n\n win.WhenFocus();\n \n this.WhenWindowAdded(win);\n \n return win;\n }\n\n // Returns true if win is found in collection\n ContainsWindow(win)\n {\n for(let w of this.windows)\n {\n if (w === win)\n return true;\n }\n \n return false;\n }\n\n // Get all windows in collection\n GetWindows()\n {\n return this.windows;\n }\n\n // Remove specified window from collection\n RemoveWindow(win)\n {\n for(let i = this.windows.length - 1; i >= 0; i--)\n {\n let w = this.windows[i];\n \n if (win === w)\n {\n this.windows.splice(i, 1);\n win.WindowClosed();\n \n this.GetTopWindow().WhenFocus();\n \n if (!this.draweveryframe)\n {\n clear();\n this.InvalidateAllWindows();\n }\n \n this.WhenWindowRemoved(win);\n }\n }\n }\n\n MinimizeWindow(win)\n {\n win.Visible = false;\n \n let nextWin = this.GetTopVisibleWindow();\n this.BringForwardWin(nextWin);\n }\n \n MaximizeWindow(win)\n {\n win.Visible = true;\n \n win.Maximized = true;\n win.SetXY(0, 0);\n win.SetSize(width, height - 40); // TODO: change the constant...\n \n this.BringForwardWin(win);\n }\n \n RestoreWindow(win)\n {\n if (!win.Visible)\n {\n win.Visible = true;\n }\n \n else if (win.Maximized)\n {\n win.Maximized = false;\n win.SetXY(win.pX, win.pY);\n win.SetSize(win.pWidth, win.pHeight);\n }\n \n this.BringForwardWin(win);\n }\n\n MaximizeOrRestoreWindow(win)\n {\n if (win.Maximized || !win.Visible)\n this.RestoreWindow(win);\n \n else\n this.MaximizeWindow(win);\n }\n\n FindWindowAtXY(x, y)\n {\n for(let i = this.windows.length - 1; i >= 0; i--)\n {\n let win = this.windows[i];\n \n if (win.Visible && win.containsMouse())\n return i;\n }\n \n return -1;\n }\n\n IsTopWindow(win)\n {\n return win === this.windows.peek();\n }\n\n IsOverlapping(win)\n {\n for(let w of this.windows)\n {\n if (win === w)\n continue;\n \n if ( w.Visible && this.Overlapping(win, w) )\n return true;\n }\n \n return false;\n }\n\n Overlapping(w1, w2)\n {\n return collisionRectRect(w1.x, w1.y, w1.w, w1.h, w2.x, w2.y, w2.w, w2.h);\n }\n\n GetTopWindow()\n {\n return this.windows.peek();\n }\n \n GetTopVisibleWindow()\n {\n for(let i = this.windows.length - 1; i >= 0; i--)\n {\n let w = this.windows[i];\n \n if (w.Visible)\n return w;\n }\n \n return null;\n }\n\n FindWindowIndex(win)\n {\n for(let i = 0; i < this.windows.length; i++)\n {\n let w = this.windows[i];\n if (win === w)\n return i;\n }\n \n return -1;\n }\n \n GetModals(win)\n {\n let ar = [];\n \n for(let i = 0; i < this.windows.length; i++)\n {\n let w = this.windows[i];\n \n if (w.Owner === win)\n ar.push(i);\n }\n \n return ar;\n }\n \n BringForwardWin(win)\n {\n let win_idx = this.FindWindowIndex(win);\n this.BringForward(win_idx);\n }\n\n BringForward(win_idx)\n {\n if (win_idx < 0 || win_idx >= this.windows.length - 1)\n return;\n\n let win = this.windows[win_idx];\n if (win.FixedWindow)\n return;\n\n let oldwin = this.windows.peek();\n if (oldwin)\n {\n oldwin.dirty = true;\n oldwin.WhenBlur();\n }\n \n this.windows.splice(win_idx, 1);\n this.windows.push(win);\n win.Visible = true;\n win.dirty = true;\n win.WhenFocus();\n\n // If window has modal, bring forward the most recent modal\n let arModals = this.GetModals(win);\n if (arModals.length > 0)\n {\n this.BringForward(arModals.peek());\n }\n }\n\n InvalidateAllWindows()\n {\n for(let win of this.windows)\n win.dirty = true;\n }\n\n InvalidateUpperOverlappingWindows(win)\n {\n let shouldInvalidate = false;\n \n if (!win.Visible)\n return;\n \n for(let w of this.windows)\n {\n if (win === w)\n {\n shouldInvalidate = true;\n continue;\n }\n \n if (shouldInvalidate && w.Visible && this.Overlapping(win, w))\n {\n w.dirty = true;\n this.InvalidateUpperOverlappingWindows(w);\n }\n }\n }\n\n // Close all auto-close windows with the exception of specified window\n CloseAutoCloseWindows(win)\n {\n let closed = false;\n \n for(let i = this.windows.length - 1; i >= 0; i--)\n {\n let w = this.windows[i];\n \n if (w !== win && w.AutoClose)\n {\n this.windows.splice(i, 1);\n closed = true;\n }\n }\n \n if (closed)\n {\n clear();\n this.InvalidateAllWindows();\n }\n }\n\n MessageBox(message, owner, callBack)\n {\n this.dialogs.MessageBox(message, owner, callBack);\n }\n \n \n // LauchApp can launch two types of apps that are provided as objects\n // - graphical sketches. Expected format { setup, loop, ... }\n // - programs for gui. Expected format { Run }\n // Note: If parameter is string, the function will do a \"require\"...\n LaunchApp(objApp, args)\n {\n if (!objApp)\n return;\n \n if (typeof objApp === \"string\")\n {\n // Specific to codeguppy...\n objApp = require(objApp);\n }\n \n // To re-evaluate how to pass arguments...\n objApp.args = args;\n \n let frm; \n let x = 250, y = 200, w = 300, h = 200;\n\n // Check if is a graphical sketch type app\n if (objApp.setup || objApp.loop)\n {\n frm = new SketchForm(x, y, w, h);\n \n frm.Text = \"Sketch\";\n frm.Sketch = objApp;\n \n if (frm.Width !== w || frm.Height !== h)\n {\n frm.X = (width - frm.Width) / 2;\n frm.Y = (height - frm.Height) / 2;\n }\n\n return this.AddForm(frm);\n }\n \n else if (objApp.Run)\n {\n // Pass the refence to ui lib \n // (only needed in codeguppy due to require implementation)\n objApp.Run(this.ui, this);\n }\n }\n\n // Subscribe to events emitted by Windows Manager\n Subscribe(event, fn)\n {\n this.events.Subscribe(event, fn);\n }\n\n // Unsubscribe from events emitted by Windows Manager\n Unsubscribe(event, fn)\n {\n this.events.Unsubscribe(event, fn);\n }\n\n OnWindowAdded(win)\n {\n }\n \n OnWindowRemoved(win)\n {\n }\n\n WhenWindowAdded(win)\n {\n win.WindowAfterDraw = (win) => {\n if (this.draweveryframe)\n return;\n\n // If there is another window on top that touches this one...\n if ( !this.IsTopWindow(win) && this.IsOverlapping(win) )\n {\n this.InvalidateUpperOverlappingWindows(win);\n }\n };\n \n win.WindowResized = (oldw, oldh, w, h) => {\n if (oldw > w || oldh > h)\n {\n if (!this.draweveryframe)\n {\n clear();\n this.InvalidateAllWindows();\n }\n }\n };\n \n win.WindowMoved = (oldx, oldy, x, y) => {\n if (oldx !== x || oldy !== y)\n {\n if (!this.draweveryframe)\n {\n clear();\n this.InvalidateAllWindows();\n }\n }\n };\n \n win.WindowVisibleChanged = () => {\n if (win.Visible)\n return;\n \n this.GetTopWindow().WhenFocus();\n \n if (!this.draweveryframe)\n {\n clear();\n this.InvalidateAllWindows();\n }\n };\n \n win.WindowMinimize = (win) => {\n this.MinimizeWindow(win);\n };\n \n win.WindowMaximizeRestore = (win) => {\n this.MaximizeOrRestoreWindow(win);\n };\n \n win.WindowClose = (win) => {\n this.RemoveWindow(win);\n };\n \n this.OnWindowAdded(win);\n this.events.Emit(\"OnWindowAdded\", [win]);\n }\n \n WhenWindowRemoved(win)\n {\n this.OnWindowRemoved(win);\n this.events.Emit(\"OnWindowRemoved\", [win]);\n }\n\n mousePressed()\n {\n let win_idx = this.FindWindowAtXY(mouseX, mouseY);\n\n if (win_idx < 0)\n {\n this.CloseAutoCloseWindows();\n return;\n }\n\n let win = this.windows[win_idx];\n \n win.mousePressed();\n \n this.CloseAutoCloseWindows(win);\n this.BringForward(win_idx);\n \n let mouseHit = win.GetMouseHit();\n if (mouseHit)\n {\n this.grabbedWindow = win;\n this.grabbedWindowDX = mouseX - this.grabbedWindow.x;\n this.grabbedWindowDY = mouseY - this.grabbedWindow.y;\n this.grabbedWindowHit = mouseHit;\n this.grabbedWindowX = this.grabbedWindow.x;\n this.grabbedWindowY = this.grabbedWindow.y;\n this.grabbedWindowW = this.grabbedWindow.w;\n this.grabbedWindowH = this.grabbedWindow.h;\n }\n }\n \n mouseReleased()\n {\n this.grabbedWindow = null;\n \n cursor(\"auto\");\n \n for(let win of this.windows)\n {\n win.mouseReleased();\n }\n }\n\n mouseDoubleClicked()\n {\n let win_idx = this.FindWindowAtXY(mouseX, mouseY);\n\n if (win_idx < 0)\n return;\n\n let win = this.windows[win_idx];\n \n win.mouseDoubleClicked();\n\n this.grabbedWindow = null;\n \n if ( win.GetMouseHit() === \"title\" && win.Sizable )\n this.MaximizeOrRestoreWindow(win);\n }\n\n dragLeft()\n {\n if (!this.grabbedWindow.Sizable)\n return;\n \n let w = this.grabbedWindowW + floor(this.grabbedWindowX - mouseX);\n if (w > 40)\n {\n this.grabbedWindow.X = floor(mouseX);\n this.grabbedWindow.Width = w;\n }\n }\n \n dragRight()\n {\n if (!this.grabbedWindow.Sizable)\n return;\n\n let w = floor(mouseX - this.grabbedWindow.x);\n if (w > 40)\n this.grabbedWindow.Width = w;\n }\n \n dragTop()\n {\n if (!this.grabbedWindow.Sizable)\n return;\n\n let h = this.grabbedWindowH + floor(this.grabbedWindowY - mouseY);\n if (h > 40)\n {\n this.grabbedWindow.Y = floor(mouseY);\n this.grabbedWindow.Height = h;\n }\n }\n \n dragBottom()\n {\n if (!this.grabbedWindow.Sizable)\n return;\n\n let h = floor(mouseY - this.grabbedWindow.y);\n if (h > 40)\n this.grabbedWindow.Height = h;\n }\n\n\n mouseDragged()\n {\n if (this.grabbedWindow && this.grabbedWindow.Maximized && this.grabbedWindowHit === \"title\")\n {\n let r = this.grabbedWindowDX / this.grabbedWindow.Width;\n this.RestoreWindow(this.grabbedWindow);\n this.grabbedWindowDX = r * this.grabbedWindow.Width;\n }\n\n if (this.grabbedWindow && !this.grabbedWindow.Maximized)\n {\n switch(this.grabbedWindowHit)\n {\n case \"title\":\n this.grabbedWindow.SetXY(mouseX - this.grabbedWindowDX, mouseY - this.grabbedWindowDY);\n break;\n \n case \"left\":\n this.dragLeft();\n break;\n \n case \"right\":\n this.dragRight();\n break;\n \n case \"top\":\n this.dragTop();\n break;\n \n case \"bottom\":\n this.dragBottom();\n break;\n \n case \"lefttop\":\n this.dragLeft();\n this.dragTop();\n break;\n \n case \"rightbottom\":\n this.dragRight();\n this.dragBottom();\n break;\n \n case \"righttop\":\n this.dragRight();\n this.dragTop();\n break;\n \n case \"leftbottom\":\n this.dragLeft();\n this.dragBottom();\n break;\n }\n }\n \n for(let win of this.windows)\n {\n if (win.containsMouse())\n win.mouseDragged();\n }\n }\n\n mouseMoved()\n {\n let win_idx = this.FindWindowAtXY(mouseX, mouseY);\n if (win_idx < 0)\n {\n cursor(\"auto\");\n return;\n }\n \n let win = this.windows[win_idx];\n if (win.Maximized || !win.Sizable)\n {\n cursor(\"auto\");\n return;\n }\n\n switch(win.GetMouseHit())\n {\n case \"title\":\n cursor(\"auto\");\n break;\n case \"left\":\n case \"right\":\n cursor(\"ew-resize\");\n break;\n case \"top\":\n case \"bottom\":\n cursor(\"ns-resize\");\n break;\n case \"lefttop\":\n case \"rightbottom\":\n cursor(\"nwse-resize\");\n break;\n case \"righttop\":\n case \"leftbottom\":\n cursor(\"nesw-resize\");\n break;\n default:\n cursor(\"auto\");\n }\n }\n\n // Handle mouse internally to simplify library hook-up\n handleMouse()\n {\n if (mouseIsPressed && !this.wasMousePressed)\n {\n this.mousePressed();\n this.wasMousePressed = true;\n \n if (this.mousePressedTime && Date.now() - this.mousePressedTime < 500 && \n this.mousePressedXY.mouseX === mouseX && this.mousePressedXY.mouseY === mouseY)\n {\n this.mousePressedTime = null;\n this.mouseDoubleClicked();\n }\n else\n {\n this.mousePressedTime = Date.now();\n }\n\n this.mousePressedXY = { mouseX, mouseY };\n }\n \n if (!mouseIsPressed && this.wasMousePressed)\n {\n this.mouseReleased();\n this.wasMousePressed = false;\n }\n \n if ( mouseX !== pmouseX || mouseY !== pmouseY )\n {\n if (mouseIsPressed)\n this.mouseDragged();\n \n else\n this.mouseMoved();\n }\n }\n \n // Handle keyboard externally (more reliably)\n keyPressed()\n {\n let win = this.GetTopWindow();\n win.keyPressed();\n }\n \n keyReleased()\n {\n let win = this.GetTopWindow();\n win.keyReleased();\n }\n\n \n}" }, { "title":"Shell", "code":"function Run(ui, wm)\n{\n let formToolbar = new ui.Form(0, 560, 800, 40);\n formToolbar.BackColor = \"LightGray\";\n formToolbar.ForeColor = \"\";\n\n let btnStart = new ui.Button(5, 5, 40, 30);\n btnStart.Text = \"💎\";\n btnStart.OnClick = OnBtnStart;\n formToolbar.AddControl(btnStart);\n\n let pnlPrograms = new ui.Panel(50, 0, 750, 40);\n pnlPrograms.BackColor = \"LightGray\";\n pnlPrograms.ForeColor = \"\";\n formToolbar.AddControl(pnlPrograms);\n \n let winToolbar = new ui.FramelessWindow(formToolbar);\n winToolbar.FixedWindow = true;\n wm.AddWindow(winToolbar);\n\n function OnBtnStart(sender, e)\n {\n let w = new ui.Form(0, 440, 150, 120);\n w.ForeColor = \"\";\n\n let winMenu = new ui.FramelessWindow(w);\n winMenu.AutoClose = true;\n\n let b1 = new ui.Button(0, 0, 150, 30);\n b1.Text = \"Sort\";\n b1.OnClick = () => { LaunchApp(\"VisualSort\", winMenu); };\n w.AddControl(b1);\n \n let b2 = new ui.Button(0, 30, 150, 30);\n b2.Text = \"Eyes\";\n b2.OnClick = () => { LaunchApp(\"Eyes\", winMenu); };\n w.AddControl(b2);\n\n let b3 = new ui.Button(0, 60, 150, 30);\n b3.Text = \"Breakout\";\n b3.OnClick = () => { LaunchApp(\"Breakout\", winMenu); };\n w.AddControl(b3);\n\n let b4 = new ui.Button(0, 90, 150, 30);\n b4.Text = \"Snake\";\n b4.OnClick = () => { LaunchApp(\"Snake\", winMenu); };\n w.AddControl(b4);\n \n wm.AddWindow(winMenu);\n }\n\n function LaunchApp(appName, winMenu)\n {\n wm.LaunchApp(appName);\n\n if (winMenu)\n winMenu.Close();\n }\n\n function AddAllWindows()\n {\n pnlPrograms.RemoveControls();\n let ar = wm.GetWindows();\n AddTaskBarButtons(ar);\n }\n \n function AddTaskBarButtons(arWindows)\n {\n let ar = arWindows.filter(w => !(w instanceof ui.FramelessWindow));\n\n let width = 75;\n let space = 2;\n \n if (ar.length * (width + space) > pnlPrograms.Width)\n width = pnlPrograms.Width / ar.length - space;\n \n for(let w of ar)\n {\n AddTaskBarButton(w, width, space);\n }\n }\n \n function AddTaskBarButton(win, width = 75, space = 2)\n {\n let n = pnlPrograms.Controls.length;\n\n let btn = new ui.Button(n * (width + space), 5, width, 30);\n btn.BackColor = \"DarkGray\";\n btn.Text = win.Text;\n btn.win = win;\n \n btn.OnClick = (sender) => {\n let win = sender.win;\n\n if (!win.Visible)\n {\n wm.RestoreWindow(win);\n }\n \n else if (win === wm.GetTopWindow()) \n {\n wm.MinimizeWindow(win);\n }\n \n else\n {\n wm.BringForwardWin(win);\n }\n };\n \n pnlPrograms.AddControl(btn);\n }\n\n wm.Subscribe(\"OnWindowAdded\", win => AddAllWindows());\n wm.Subscribe(\"OnWindowRemoved\", win => AddAllWindows());\n}" }, { "title":"prg1", "code":"function Run(ui, wm)\n{\n let counter = 0;\n \n let win2 = new ui.Form(180, 100, 300, 200);\n win2.Text = \"Window 2\";\n\n let win3 = new ui.Form(450, 300, 200, 180);\n win3.Text = \"Window 3\";\n\n let btnf = new ui.FlatButton(10, 10, 100, 20);\n btnf.Text = \"[ Open Sketch ]\";\n btnf.OnClick = (sender) => {\n // let frm = new ui.SketchForm(10, 10, 320, 200);\n // frm.Text = txt1.Text + \" \" + txt2.Text;\n // frm.Sketch = require(\"prg3\");\n // wm.AddForm(frm);\n \n // Can use Launch app instead of creating form / window manually\n wm.LaunchApp(\"prg3\");\n };\n \n let lbl = new ui.Label(10, 40, 100, 40);\n lbl.Text = \"Hello, World! This is a nice label...\";\n lbl.Subscribe(\"OnClick\", (sender) => {\n console.log(sender.Text); \n });\n\n let txt1 = new ui.TextBox(10, 80, 100, 20);\n txt1.Text = \"Hello\";\n txt1.OnTextChanged = (sender, e) => {\n console.log(sender.Text);\n }\n\n let txt2 = new ui.TextBox(10, 110, 100, 20);\n txt2.Text = \"World\";\n \n \n let bs1 = new ui.Button(140, 10, 20, 20);\n bs1.Text = \"1\";\n bs1.OnClick = () => {\n win2.SetSize(400, 300);\n };\n\n let bs1w = new ui.Button(170, 10, 20, 20);\n bs1w.Text = \"1w\";\n bs1w.OnClick = () => {\n wwin2.SetSize(400, 300)\n };\n\n let bs2 = new ui.Button(140, 30, 20, 20);\n bs2.Text = \"2\";\n bs2.OnClick = () => {\n win2.SetSize(300, 200);\n };\n\n let bs2w = new ui.Button(170, 30, 20, 20);\n bs2w.Text = \"2w\";\n bs2w.OnClick = () => {\n wwin2.SetSize(300, 200);\n };\n \n let bmin = new ui.Button(140, 60, 50, 20);\n bmin.Text = \"Minimize\";\n bmin.OnClick = () => {\n wm.MinimizeWindow(wwin2);\n };\n\n let brestore = new ui.Button(140, 80, 50, 20);\n brestore.Text = \"Restore\";\n brestore.OnClick = () => {\n wm.RestoreWindow(wwin2);\n //wm.MaximizeOrRestoreWindow(wwin2);\n };\n\n let bmax = new ui.Button(140, 100, 50, 20);\n bmax.Text = \"Maximize\";\n bmax.OnClick = () => {\n wm.MaximizeWindow(wwin2);\n };\n\n let slider = new ui.Slider(10, 150, 180);\n slider.Minimum = 90;\n slider.Maximum = 100;\n slider.Value = 95;\n\n win3.AddControl(btnf);\n win3.AddControl(lbl);\n win3.AddControl(txt1);\n win3.AddControl(txt2);\n \n win3.AddControl(bs1);\n win3.AddControl(bs2);\n win3.AddControl(bs1w);\n win3.AddControl(bs2w);\n \n win3.AddControl(bmin);\n win3.AddControl(brestore);\n win3.AddControl(bmax);\n \n win3.AddControl(slider);\n\n let canvas = new ui.Canvas(0, 0, 300, 200);\n win2.AddControl(canvas);\n \n win2.Subscribe(\"OnResize\", (sender, e) => {\n canvas.SetSize(e.Width, e.Height);\n });\n\n let cx = 100; let dcx = 1;\n let cy = 100; let dcy = 1;\n\n //canvas.Sketch = require(\"prg3\");\n \n canvas.OnLoop = () => {\n let g = canvas.Gfx; \n \n g.clear();\n g.circle(cx, cy, 15);\n cx += dcx;\n cy += dcy;\n if(cx > g.width || cx < 0)\n dcx *= -1;\n if(cy > g.height || cy < 0)\n dcy *= -1;\n \n g.text(g.width + \" x \" + g.height, 10, 14);\n \n canvas.invalidate();\n };\n\n let btn1 = new ui.Button(10, 10, 140, 20);\n btn1.Text = \"Change Window Title\";\n btn1.OnClick = (sender) => {\n console.log(\"Button \" + sender.Text + \" clicked\");\n win3.Text = \"Nice window: \" + counter++;\n };\n \n let btn2 = new ui.Button(10, 40, 140, 30);\n btn2.Text = \"Change Button Text\";\n btn2.OnClick = (sender) => {\n btn1.Text = \"Hi \" + counter++;\n \n let fruit = \"\";\n if (radio1.Checked) fruit = radio1.Text;\n if (radio2.Checked) fruit = radio2.Text;\n if (radio3.Checked) fruit = radio3.Text;\n if (radio4.Checked) fruit = radio4.Text;\n if (radio5.Checked) fruit = radio5.Text;\n\n win.Enabled = false;\n\n wm.MessageBox(\"I like to eat\\n\" + fruit , win, () => {\n win.Enabled = true;\n });\n };\n\n let chk1 = new ui.CheckBox(10, 80, 140, 20);\n chk1.Text = \"Do you like this?\";\n chk1.OnCheckedChanged = () => {console.log(\"Checkbox...\")};\n\n let radio1 = new ui.RadioButton(10, 110, 100, 20);\n radio1.Checked = true;\n radio1.Text = \"Apples\";\n\n let radio2 = new ui.RadioButton(10, 130, 100, 20);\n radio2.Text = \"Oranges\";\n\n let radio3 = new ui.RadioButton(10, 150, 100, 20);\n radio3.Text = \"Bananas\";\n\n let radio4 = new ui.RadioButton(100, 110, 100, 20);\n radio4.Text = \"Grapes\";\n\n let radio5 = new ui.RadioButton(100, 130, 100, 20);\n radio5.Text = \"Strawberries\";\n\n let win = new ui.Form(10, 10, 200, 180);\n win.Text = \"Buttons\";\n win.AddControl(btn1);\n win.AddControl(btn2);\n win.AddControl(chk1);\n win.AddControl(radio1);\n win.AddControl(radio2);\n win.AddControl(radio3);\n win.AddControl(radio4);\n win.AddControl(radio5);\n \n let wwin1 = new ui.Window(win);\n //wwin1.MinimizeButton = false;\n //wwin1.MaximizeButton = false;\n wwin1.Sizable = false;\n wm.AddWindow(wwin1);\n\n let wwin2 = new ui.Window(win2);\n wm.AddWindow(wwin2);\n \n wm.AddForm(win3);\n\n setInterval(() => {\n btn2.Text = \"Number \" + counter++;\n }, 1000);\n}" }, { "title":"prg3", "code":"let cx = 0; \nlet cy = 0; \n\nlet dcx = 1;\nlet dcy = 1;\n\nlet dots = [];\n\nfunction setup(g)\n{\n // You can use these (optional) if you want the sketch\n // to control the outside canvas control / window\n g.Text = \"Bouncing ball\";\n g.resizeCanvas(400, 300);\n}\n\nfunction resize(g)\n{\n if (cx > g.width)\n cx = g.width;\n \n if (cy > g.height)\n cy = g.height;\n}\n\nfunction loop(g)\n{\n g.clear();\n\n g.noFill();\n g.stroke(\"Black\");\n g.circle(cx, cy, 15);\n\n cx += dcx;\n cy += dcy;\n\n if(cx > g.width || cx < 0)\n dcx *= -1;\n\n if(cy > g.height || cy < 0)\n dcy *= -1;\n\n g.fill(\"Tan\");\n for(let dot of dots)\n g.circle(dot[0], dot[1], 10);\n\n g.fill(\"Black\");\n g.noStroke();\n \n g.text(g.width + \" x \" + g.height, g.width - 80, 12);\n g.text(floor(g.mouseX) + \" x \" + floor(g.mouseY), 12, 12);\n}\n\nfunction mousePressed(g)\n{\n dots.push( [g.mouseX, g.mouseY] );\n}\n\nfunction mouseReleased(g)\n{\n}\n\nfunction keyPressed(g)\n{\n if (key === \"r\")\n dots.length = 0;\n}\n\nfunction keyReleased(g)\n{\n}" }, { "title":"VisualSort", "code":"let ar = [];\nlet sortRound;\n\nlet maxno;\n\nfunction setup(g)\n{\n g.resizeCanvas(500, 300);\n g.Text = \"Visual Sort\";\n \n g.textSize(8);\n}\n\nfunction loop(g)\n{\n if (waiting(100))\n return;\n \n g.clear();\n \n if ( !sortArray(ar) )\n {\n maxno = g.height;\n ar = generateArray( g.width / 8, 0, maxno );\n sortRound = 1;\n }\n\n displayArray(g, ar);\n}\n\n\nfunction sortArray(ar)\n{\n var sortPerformed = false;\n \n for(var i = 0; i < ar.length - 1; i++)\n {\n var a = ar[i].n;\n if (a > ar[i+1].n)\n {\n ar[i].n = ar[i+1].n;\n ar[i+1].n = a;\n \n ar[i].color = \"red\";\n\n sortPerformed = true;\n }\n else\n {\n ar[i].color = \"black\";\n }\n }\n\n return sortPerformed;\n}\n\nfunction generateArray(n, x1, x2)\n{\n var ar = [];\n\n for(var i = 0; i < n; i++)\n {\n ar.push( { n : round(random(x1, x2)), color: \"black\" } );\n }\n\n return ar;\n}\n\n\nfunction displayArray(g, ar)\n{\n var n = ar.length;\n \n for(var i = 0; i < n; i++)\n {\n var no = ar[i].n;\n \n var x = map(i, 0, n, 0, g.width);\n \n g.strokeWeight(2);\n g.stroke(ar[i].color);\n g.line(x, g.height - map(no, 0, maxno, 0, g.height), x, g.height);\n\n var r = floor(i / 15);\n var c = i - r * 15;\n\n g.stroke(\"white\");\n g.fill(ar[i].color);\n g.text(no, 5 + c * 20, r * 12 + 20);\n }\n\n g.stroke(\"white\");\n g.text(\"Round: \" + sortRound++, 5, 10);\n}\n\nlet waitStart = null;\n\nfunction waiting(ms)\n{\n if (ms === 0)\n {\n waitStart = null;\n return;\n }\n \n if (!waitStart)\n {\n waitStart = Date.now();\n }\n \n if (Date.now() - waitStart > ms)\n {\n waitStart = null;\n return false;\n }\n \n return true;\n}" }, { "title":"Eyes", "code":"let g;\n\nfunction setup(gfx)\n{\n g = gfx;\n\n g.resizeCanvas(200, 100);\n g.Text = \"Eyes following mouse\";\n}\n\nfunction loop()\n{\n let r = g.width / 5;\n let space = r / 3;\n \n g.clear();\n \n drawEye(space + r, g.height / 2, r);\n drawEye(2 * space + 3 * r, g.height / 2, r);\n}\n\nfunction drawEye(x, y, r)\n{\n g.fill(\"white\");\n g.ellipse(x, y, r * 2);\n\n drawEyePupil(x, y, 0.75 * r, 0.5 * r);\n}\n\n// Draw eyes that follow the mouse position\nfunction drawEyePupil(x1, y1, r, pr)\n{\n let angle = atan2(g.mouseY - y1, g.mouseX - x1);\n\n let x2 = x1 + r * cos(angle);\n let y2 = y1 + r * sin(angle);\n\n g.fill(\"black\");\n g.ellipse(x2, y2, pr);\n}" }, { "title":"Breakout", "code":"let paddleWidth = 60;\nlet brickWidth = 50;\nlet brickHeight = 20;\nlet brickSpace = 20;\nlet rowSpace = 10;\n\nlet xPaddle;\nlet yPaddle;\n\nlet ball;\nlet ballsLeft;\n\nlet bricks = [];\n\nlet g;\n\nfunction setup(gfx)\n{\n g = gfx;\n \n g.resizeCanvas(640, 400);\n g.Text = \"Breakout\";\n \n g.fill(\"white\");\n initGame();\n}\n\nfunction resize()\n{\n initGame();\n}\n\nfunction initGame()\n{\n bricks = createBricks();\n ballsLeft = 3;\n \n initBall();\n}\n\nfunction initBall()\n{\n xPaddle = g.width / 2;\n yPaddle = g.height - 20;\n\n ball = {\n radius : 5,\n x : 0,\n y : 0,\n xvel : 5,\n yvel : -5,\n inMotion : false\n };\n}\n\nfunction loop()\n{\n g.background(\"Orange\");\n\n readKeys();\n displayBricks();\n displayPaddle();\n\n updateBall();\n displayBall();\n\n checkForCollision();\n\n displayStats();\n}\n\nfunction displayStats()\n{\n g.push();\n g.fill('black');\n g.noStroke();\n\n g.text(\"Balls: \" + ballsLeft, 10, g.height - 20);\n g.text(\"Bricks: \" + bricks.length, 10, g.height - 6);\n\n g.pop();\n}\n\nfunction updateBall()\n{\n if ( !ball.inMotion )\n {\n ball.x = xPaddle + paddleWidth / 2;\n ball.y = yPaddle - ball.radius;\n }\n else\n {\n updateBallInMotion();\n }\n}\n\nfunction updateBallInMotion()\n{\n ball.x += ball.xvel;\n ball.y += ball.yvel;\n\n if ( ball.x < 0 || ball.x > g.width )\n {\n ball.xvel *= -1;\n }\n else if ( ball.y < 0 )\n {\n ball.yvel *= -1;\n }\n else if ( collisionCircleRect(ball.x, ball.y, ball.radius, xPaddle, yPaddle, paddleWidth, 10) )\n {\n ball.yvel *= -1;\n }\n else if ( ball.y > g.height )\n {\n ballsLeft--;\n \n if (ballsLeft >= 0)\n {\n initBall();\n }\n else\n {\n initGame();\n }\n }\n}\n\nfunction checkForCollision()\n{\n let brickIndex = getHitBrick();\n if ( brickIndex == -1 )\n {\n return;\n }\n\n bricks.splice(brickIndex, 1);\n ball.yvel *= -1;\n\n if ( bricks.length === 0 )\n {\n initGame();\n }\n}\n\n\n// Iterate through all the bricks and check\n// if the ball hits any bricks.\n// Returns the index of the brick that is hit ... or -1\nfunction getHitBrick()\n{\n for(let i = 0; i < bricks.length; i++)\n {\n let brick = bricks[i];\n\n if ( collisionCircleRect( ball.x, ball.y, ball.radius, brick.x, brick.y, brickWidth, brickHeight ) )\n {\n return i;\n }\n }\n\n return -1;\n}\n\nfunction displayBall()\n{\n g.ellipse( ball.x, ball.y, ball.radius * 2 );\n}\n\nfunction readKeys()\n{\n if ( g.keyIsDown( LEFT_ARROW ) && xPaddle > 0 )\n {\n xPaddle -= 5;\n }\n else if ( g.keyIsDown( RIGHT_ARROW) && xPaddle < g.width - paddleWidth )\n {\n xPaddle += 5;\n }\n else if ( g.keyIsDown (32) ) // SPACE\n {\n ball.inMotion = true;\n }\n}\n\nfunction createBricks()\n{\n let noBricks = Math.floor((g.width - brickSpace) / ( brickWidth + brickSpace ));\n let arBricks = [];\n\n for(let row = 0; row < 3; row++)\n { \n for(let col = 0; col < noBricks; col++ )\n {\n let x = col * ( brickWidth + brickSpace ) + brickSpace;\n let y = row * (brickHeight + rowSpace) + rowSpace;\n \n let brick = { x : x, y : y };\n arBricks.push(brick);\n }\n }\n\n return arBricks;\n}\n\nfunction displayBricks()\n{\n for(let i = 0; i < bricks.length; i++)\n {\n let brick = bricks[i];\n g.rect( brick.x, brick.y, brickWidth, brickHeight );\n }\n}\n\nfunction displayPaddle()\n{\n g.rect( xPaddle, yPaddle, paddleWidth, 10 );\n}" }, { "title":"Snake", "code":"let g;\n\nlet squareSize;\nlet noRows;\nlet noCols;\n\n// Set the game speed (the lower number, the bigger speed.)\nlet speedDelay = 100;\n\n// Direction in which the snake is heading\nlet dx = 1;\nlet dy = 0;\n\n// Segments of the snake body (first one is the head)\nlet segments = [];\n\nlet food;\n\nlet bestScore = 0;\nlet score = 0;\n\nlet lastUpdate;\nlet lastEat;\n\nlet displayOpen = true;\n\nlet crashed = false;\n\nfunction resize()\n{\n calculateSquareSize();\n}\n\nfunction setup(gfx)\n{\n g = gfx;\n g.angleMode(DEGREES);\n \n g.resizeCanvas(640, 400);\n g.Text = \"Snake\";\n \n init();\n}\n\nfunction calculateSquareSize()\n{\n squareSize = min(g.width, g.height) / 20;\n}\n\nfunction init()\n{\n //music('Polka Train', 0.1);\n\n calculateSquareSize();\n noRows = floor((g.height - 20) / squareSize);\n noCols = floor(g.width / squareSize);\n\n if (score > bestScore)\n bestScore = score;\n\n crashed = false;\n score = 0;\n lastEat = Date.now();\n\n dx = 1;\n dy = 0;\n \n segments = [];\n grow();\n \n addFood();\n}\n\nfunction loop()\n{\n update();\n display(); \n}\n\nfunction update()\n{\n if (crashed)\n {\n if (keyIsDown(82)) // R\n init();\n\n return;\n }\n\n checkKeys();\n updateSnake();\n checkEat();\n checkCollision();\n}\n\nfunction display()\n{\n g.background('MintCream');\n\n displayArena();\n displaySnake();\n displayFood();\n displayStats()\n\n if (crashed)\n {\n g.push();\n g.fill(\"black\");\n g.textSize(20);\n g.textAlign(CENTER, CENTER);\n g.text(\"You crashed!\", g.width / 2, g.height / 2);\n \n g.textSize(12);\n g.text(\"Press R to restart game\", g.width / 2, g.height / 2 + 40);\n \n if (score > bestScore && displayOpen)\n {\n g.fill(\"teal\");\n g.text(\"You have a new best score!\", g.width / 2, g.height / 2 + 20);\n }\n g.pop();\n \n //music(\"\");\n }\n}\n\nfunction updateSnake()\n{\n if (segments.length === 0)\n return;\n\n if (Date.now() - lastUpdate < speedDelay)\n return;\n\n lastUpdate = Date.now();\n \n // implement snake update / move, similar to grow\n \n // when snake is moving is growing towards head and drops the tail\n let row = segments[0].row + dy;\n let col = segments[0].col + dx;\n\n let newSegment = { row, col };\n segments.unshift(newSegment);\n\n // remove last segment\n segments.pop();\n}\n\nfunction checkKeys()\n{\n if (g.keyIsDown(LEFT_ARROW))\n {\n dx = -1;\n dy = 0;\n }\n \n else if (g.keyIsDown(RIGHT_ARROW))\n {\n dx = 1;\n dy = 0;\n }\n\n else if (g.keyIsDown(UP_ARROW))\n {\n dx = 0;\n dy = -1;\n }\n\n else if (g.keyIsDown(DOWN_ARROW))\n {\n dx = 0;\n dy = 1;\n }\n}\n\nfunction displaySnake()\n{\n displaySnakeHead();\n for(let i = 1; i < segments.length; i++)\n {\n displaySnakeSegment(i);\n }\n}\n\nfunction displaySnakeSegment(no)\n{\n g.noStroke();\n g.fill(\"teal\");\n\n let segment = segments[no];\n\n let x = segment.col * squareSize;\n let y = segment.row * squareSize;\n let r = squareSize / 2;\n\n g.circle(x + squareSize / 2, y + squareSize / 2, r);\n}\n\nfunction displaySnakeHead()\n{\n g.noStroke();\n g.fill(\"teal\");\n\n let x, y, a1, a2;\n let segment = segments[0];\n \n x = segment.col * squareSize + squareSize / 2;\n y = segment.row * squareSize + squareSize / 2;\n\n if (dx === 1 && dy === 0)\n {\n a1 = 30;\n a2 = 330;\n }\n\n else if (dx === -1 && dy === 0)\n {\n a1 = 210;\n a2 = 150;\n }\n\n else if (dy === -1 && dx === 0)\n {\n a1 = 300;\n a2 = 240;\n }\n\n else if (dy === 1 && dx === 0)\n {\n a1 = 120;\n a2 = 60;\n }\n\n if (frameCount % 10 === 0)\n displayOpen = !displayOpen;\n\n if (!displayOpen)\n {\n a1 = 0;\n a2 = 359;\n }\n\n g.arc(x, y, squareSize, squareSize, a1, a2);\n}\n\n\nfunction grow()\n{\n let row = 0;\n let col = 0;\n\n if (segments.length > 0)\n {\n row = segments[0].row + dy;\n col = segments[0].col + dx;\n }\n \n // A new segment (e.g. head) is added at the beginning of the \n // array (e.g. snake) in the direction that is moving\n let newSegment = { row, col };\n segments.unshift(newSegment);\n}\n\nfunction addFood()\n{\n let row = randomInt(0, noRows - 1);\n let col = randomInt(0, noCols - 1);\n \n food = { row, col }\n}\n\nfunction displayFood()\n{\n g.fill(\"red\");\n g.noStroke();\n \n let x = food.col * squareSize;\n let y = food.row * squareSize;\n \n let d = 3 * sin(frameCount);\n\n g.rect(x + 3 + d, y + 3 + d, squareSize - 5 - d * 2, squareSize - 5 - d * 2);\n}\n\nfunction checkEat()\n{\n if ( segments[0].row === food.row &&\n\n segments[0].col === food.col )\n {\n updateScore();\n lastEat = Date.now();\n sound(\"pepSound1\");\n\n grow();\n addFood();\n }\n}\n\nfunction updateScore()\n{\n // Get a score multiplication factor \"> 1\" if eat food in less than 10 seconds\n function getScoreMultiplication()\n {\n let norm = 10000;\n let delta = Date.now() - lastEat\n \n if (delta > norm)\n return 1;\n \n return floor((norm - delta) / 1000);\n }\n \n // Score is updated based on snake length and multiplication factor\n let deltaScore = segments.length * 10 * getScoreMultiplication();\n \n // Add more to the score if food was close to the wall\n if (food.row === 0 || food.row === noRows - 1 ||\n food.col === 0 || food.col === noCols - 1)\n deltaScore *= 1.5;\n \n score += floor(deltaScore);\n}\n\nfunction checkCollision()\n{\n let head = segments[0];\n \n // Check collisions with walls\n if (head.row < 0 || head.row >= noRows ||\n head.col < 0 || head.col >= noCols)\n {\n crashed = true;\n return;\n }\n \n // Check collisions with body segments\n for(let i = 1; i < segments.length; i++)\n {\n let segment = segments[i];\n \n if (head.row === segment.row && \n head.col === segment.col)\n {\n crashed = true;\n return;\n }\n }\n}\n\nfunction displayArena()\n{\n g.noFill();\n\n g.stroke(\"black\");\n g.rect(0, 0, noCols * squareSize, noRows * squareSize);\n\n g.stroke(\"DarkSeaGreen\");\n \n for(let row = 0; row < noRows; row++)\n {\n g.line(0, row * squareSize, noCols * squareSize, row * squareSize);\n }\n \n for(let col = 0; col < noCols; col++)\n {\n g.line(col * squareSize, 0, col * squareSize, noRows * squareSize);\n }\n}\n\nfunction displayStats()\n{\n g.fill(\"black\");\n \n g.textSize(12);\n g.text(\"Use arrow keys to change snake direction!\", 10, g.height - 4);\n g.text(\"Best Score: \" + bestScore, g.width / 2, g.height - 4);\n g.text(\"Score: \" + score, g.width - 100, g.height - 4);\n}" } ]