(loadFrom "kiss-tools" "src/kiss_tools/RefactorUtil.kiss") // All windows share the same text size (var &mut textSize 16) (var :kiss.List windowStack []) (var &mut :flixel.FlxCamera defaultCamera null) (prop :FlxCamera controlCamera) (prop &mut keyboardEnabled true) // TODO tooltip support with left-click and right-click action // icons and explanations? (defNew [&opt :String _title :FlxColor bgColor :FlxColor _textColor :Float percentWidth :Float percentHeight :Bool _xButton :String _xKey :ShortcutAction _onClose] [:String title (or _title "") &mut :Float nextControlX 0 &mut :Float nextControlY 0 &mut :Int controlsPerColumn 0 :FlxColor titleColor (or _textColor FlxColor.WHITE) &mut :FlxColor textColor (or _textColor FlxColor.WHITE) :Bool xButton ?_xButton :String xKey _xKey :ShortcutAction onClose _onClose :FlxTypedGroup controls (new FlxTypedGroup) :FlxKeyShortcutHandler keyHandler (new FlxKeyShortcutHandler) :FlxKeyShortcutHandler xHandler (new FlxKeyShortcutHandler)] (super 0 0) (when defaultCamera (set this.cameras [defaultCamera])) (makeGraphic (Std.int (* FlxG.width (or percentWidth 0.5))) (Std.int (* FlxG.height (or percentHeight 0.5))) (or bgColor FlxColor.BLACK)) (screenCenter) (set controlCamera (new FlxCamera (Std.int x) (Std.int y) (Std.int width) (Std.int height))) (set controlCamera.bgColor FlxColor.TRANSPARENT) // Top-left corner for controls is (0,0) because a camera displays them (set nextControlX 0) (set nextControlY 0) (let [textHeight .height (new FlxText 0 0 0 "a" textSize)] (set controlsPerColumn (Math.floor (/ height textHeight))) (-= controlsPerColumn 1) // Column at the bottom for left/right scroll arrows (when title (-= controlsPerColumn 1))) (when title (makeText title null)) (set keyHandler.onBadKey ->:Void [key context] (unless (= key xKey) (print "bad key $key in context $context"))) // TODO do SOMETHING like visual/audio bell, cancel and restart (set keyHandler.onSelectItem ->:Void [:ShortcutAction a] { (a) (keyHandler.start) }) (set xHandler.cancelKey null) (set xHandler.onBadKey ->:Void [key context] 0) (set xHandler.onSelectItem ->:Void [:ShortcutAction a] { (a) (xHandler.start) }) (defAndCall method makeXControls (let [closeAction ->:Void {(hide)(when onClose (onClose))}] (when xButton (let [ftext (new FlxText width 0 0 "X" textSize)] (set ftext.cameras [controlCamera]) (-= ftext.x ftext.width) (set ftext.color textColor) (dictSet _colors ftext ftext.color) (dictSet _actions ftext ->:Void _ (closeAction)) (set xText ftext) (controls.add xText))) (when xKey (when (= keyHandler.cancelKey xKey) (set keyHandler.cancelKey null)) (xHandler.registerItem "{${xKey}}" closeAction)))) // TODO show which shortcuts' prefixes are partially highlighted? ) (prop :FlxTypedGroup inputTexts (new FlxTypedGroup)) (method addControl [:FlxSprite control] (when (Std.isOfType control FlxInputText) (inputTexts.add (cast control FlxInputText))) (set control.cameras [controlCamera]) (set control.x nextControlX) (set control.y nextControlY) (controls.add control) (+= nextControlY control.height) // TODO controls that aren't the same height as text will be able to vertically overflow (let [columnControls (controls.members.slice (if title 1 0))] (setNth columnWidths -1 (max (+ control.width textSize) (last columnWidths))) (when (and columnControls (= 0 (% columnControls.length controlsPerColumn))) (set nextControlY 0) (when title (+= nextControlY control.height)) (+= nextControlX (last columnWidths)) (columnWidths.push 0) (when (> (apply + columnWidths) width) (makeScrollArrows)))) control) (prop &mut :FlxText leftText) (prop &mut :FlxText rightText) (prop &mut :FlxText xText) (method makeScrollArrows [] (unless hasScrollArrows // The left arrow control is not added until the window scrolls right (let [ftext (new FlxText 0 height 0 "<-" textSize)] (set ftext.cameras [controlCamera]) (-= ftext.y ftext.height) (set ftext.color textColor) (dictSet _colors ftext ftext.color) (dictSet _actions ftext ->:Void _ (scrollLeft)) (set leftText ftext)) (let [ftext (new FlxText width height 0 "->" textSize)] (set ftext.cameras [controlCamera]) (-= ftext.x ftext.width) (-= ftext.y ftext.height) (set ftext.color textColor) (dictSet _colors ftext ftext.color) (controls.add ftext) (dictSet _actions ftext ->:Void _ (scrollRight)) (set rightText ftext)) (set hasScrollArrows true))) (prop :Map _actions (new Map)) (prop :Map _colors (new Map)) (method makeText [:String text &opt :FlxColor color :Action onClick :Bool noShortcut] (let [ftext (new FlxText nextControlX nextControlY 0 text textSize)] (set ftext.color (or color textColor)) (dictSet _colors ftext ftext.color) (addControl ftext) (when onClick (dictSet _actions ftext onClick) // TODO right click? (unless noShortcut (keyHandler.registerItem text ->:Void (onClick ftext)))) ftext)) // TODO makeButton // TODO make inputText (prop &mut _shown false) (method isShown [] _shown) (prop &mut :kiss.List columnWidths [0.0]) (prop &mut cameraColumn 0) (prop &mut hasScrollArrows false) (method clearControls [] (set columnWidths [0.0]) (set hasScrollArrows false) (_actions.clear) (controls.clear) (inputTexts.clear) (keyHandler.clear) (makeXControls) (set nextControlX 0) (set nextControlY 0) (makeText title titleColor)) (method :Void show [] (unless _shown (FlxG.cameras.add controlCamera) (FlxG.state.add this) (FlxG.state.add controls) (windowStack.push this) (keyHandler.start) (xHandler.start) (set _shown true))) (method :Void hide [] (when _shown (FlxG.cameras.remove controlCamera false) (FlxG.state.remove this) (FlxG.state.remove controls) (windowStack.remove this) (keyHandler.cancel) (xHandler.cancel) (set _shown false))) (function getHighlighted [:FlxColor color &opt :Float amount] (unless amount (set amount 0.2)) (cond ((> color.lightness amount) (color.getDarkened amount)) (true (color.getLightened amount)))) (prop &mut otherIsSelected false) (method &override update [:Float elapsed] (super.update elapsed) (set otherIsSelected false) (when (= (last windowStack) this) (when keyboardEnabled (unless (apply or (for textBox inputTexts.members textBox.hasFocus)) (keyHandler.update))) (xHandler.update) // Handle mouse input (let [mousePos (FlxG.mouse.getScreenPosition controlCamera)] (controls.forEach ->text (whenLet [onClick (dictGet _actions text)] (let [color (dictGet _colors text)] (if (and !otherIsSelected (.containsPoint (text.getScreenBounds controlCamera) mousePos)) { (when FlxG.mouse.justPressed (onClick text)) (set otherIsSelected true) (set text.color (getHighlighted color)) } { (set text.color color) }))))))) (function :SimpleWindow promptForChoice <>[T] [:String prompt :Array choices :T->Void onChoice &opt :FlxColor bgColor :FlxColor titleColor :FlxColor choiceColor :Float percentWidth :Float percentHeight :Bool xButton :String xKey :ShortcutAction onClose :Bool noShortcuts] (let [window (new SimpleWindow prompt bgColor titleColor percentWidth percentHeight xButton xKey onClose) choiceColor (or choiceColor titleColor FlxColor.WHITE)] (doFor choice choices (window.makeText (Std.string choice) choiceColor ->:Void s { (window.hide) (onChoice choice) } noShortcuts)) (window.show) window)) (method scrollLeft [] (when (> cameraColumn 0) (-= cameraColumn 1) (when (= cameraColumn 0) (controls.remove leftText)) (controls.add rightText) (let [scrollAmount (nth columnWidths cameraColumn)] (-= controlCamera.scroll.x scrollAmount) (-= leftText.x scrollAmount) (-= rightText.x scrollAmount) (when xText (-= xText.x scrollAmount))))) (method scrollRight [] (when (< cameraColumn (- columnWidths.length 1 )) (let [scrollAmount (nth columnWidths cameraColumn)] (+= controlCamera.scroll.x scrollAmount) (+= leftText.x scrollAmount) (+= rightText.x scrollAmount) (when xText (+= xText.x scrollAmount))) (+= cameraColumn 1) (when (< (apply + (columnWidths.slice cameraColumn)) width) (controls.remove rightText)) (controls.add leftText))) // Irreversibly disable the window's buttons (for when you're going to hide it in the next frame) (method clearActions [] (_actions.clear))