Files
kiss-flixel/src/kiss_flixel/SimpleWindow.kiss

354 lines
14 KiB
Plaintext

(loadFrom "kiss-tools" "src/kiss_tools/RefactorUtil.kiss")
// All windows share the same text size
(var &mut textSize 16)
(var :kiss.List<SimpleWindow> 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<FlxSprite> controls (new FlxTypedGroup)
:FlxKeyShortcutHandler<ShortcutAction> keyHandler (new FlxKeyShortcutHandler)
:FlxKeyShortcutHandler<ShortcutAction> 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
(set titleText (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<KissInputText> inputTexts (new FlxTypedGroup))
(method addControl [:FlxSprite control]
(when (Std.isOfType control KissInputText)
(inputTexts.add (cast control KissInputText)))
(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))]
// Don't count special controls as part of any column:
(doFor c [xText leftText rightText]
(when c (columnControls.remove c)))
(doFor c columnTexts
(when c (columnControls.remove c)))
(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 titleText)
(prop &mut :FlxText leftText)
(prop &mut :FlxText rightText)
(prop &mut :Array<FlxText> columnTexts [])
(prop &mut :FlxText xText)
(method columnTextStr [:Int column]
(if (= cameraColumn column) ">${column}<" "$column"))
(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))
(refreshColumnTexts)
(set hasScrollArrows true))
// A column could be added while the same window is shown.
(refreshColumnTexts))
(method refreshColumnTexts []
(doFor i (range columnWidths.length)
(unless (> columnTexts.length i)
(let [ftext (new FlxText (fHalf width) height 0 (columnTextStr i) textSize)]
(set ftext.cameras [controlCamera])
(-= ftext.x (fHalf ftext.width))
(-= ftext.y ftext.height)
(set ftext.color textColor)
(dictSet _colors ftext ftext.color)
(dictSet _actions ftext
->:Void _
(until (= cameraColumn i)
(if (< cameraColumn i)
(scrollRight)
(scrollLeft))))
(controls.add ftext)
(columnTexts.push ftext)))
(let [ftext (nth columnTexts i)]
(set ftext.text (columnTextStr i))
(set ftext.x (+ (fHalf width) controlCamera.scroll.x))
(-= ftext.x (* (- (fHalf columnWidths.length) i) textSize 3))
(when (= cameraColumn i) (-= ftext.x .width (new FlxText 0 0 0 ">" textSize))))))
(prop :Map<FlxSprite,Action> _actions (new Map))
(prop :Map<FlxSprite,FlxColor> _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<Float> columnWidths [0.0])
(prop &mut cameraColumn 0)
(prop &mut hasScrollArrows false)
(method clearControls []
(set columnWidths [0.0])
(set columnTexts [])
(set hasScrollArrows false)
(_actions.clear)
(controls.clear)
(inputTexts.clear)
(keyHandler.clear)
(xHandler.clear)
(makeXControls)
(set nextControlX 0)
(set nextControlY 0)
(set titleText (makeText title titleColor)))
(method :Void show [&opt :Int _cameraColumn]
(when (and _cameraColumn !(= cameraColumn _cameraColumn))
(assert (<= 0 _cameraColumn (- columnWidths.length 1)) "Tried to show out-of-bounds camera column ${_cameraColumn} of ${columnWidths.length}")
(while (> cameraColumn _cameraColumn)
(scrollLeft))
(while (< cameraColumn _cameraColumn)
(scrollRight)))
(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)]
// Click and hover on clickable text entries
(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)
}))))
// Click on text boxes to focus them
(inputTexts.forEach ->text
(when FlxG.mouse.justPressed
(when (.containsPoint (text.getScreenBounds controlCamera) mousePos)
(inputTexts.forEach ->text (set text.hasFocus false))
(set otherIsSelected true)
(set text.caretIndex (text.getCaretIndex controlCamera))
(set text.hasFocus true))))
// Click anywhere else to take focus away from text boxes
(when (and !otherIsSelected FlxG.mouse.justPressed)
(inputTexts.forEach ->text (set text.hasFocus false))))))
(function :SimpleWindow promptForChoice <>[T] [:String prompt
:Array<T> 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)
(when titleText
(-= titleText.x scrollAmount))
(-= leftText.x scrollAmount)
(-= rightText.x scrollAmount)
(doFor columnText columnTexts
(-= columnText.x scrollAmount))
(when xText
(-= xText.x scrollAmount)))
(refreshColumnTexts)))
(method scrollRight []
(when (< cameraColumn (- columnWidths.length 1 ))
(let [scrollAmount (nth columnWidths cameraColumn)]
(+= controlCamera.scroll.x scrollAmount)
(when titleText
(+= titleText.x scrollAmount))
(+= leftText.x scrollAmount)
(+= rightText.x scrollAmount)
(doFor columnText columnTexts
(+= columnText.x scrollAmount))
(when xText
(+= xText.x scrollAmount)))
(+= cameraColumn 1)
(when (< (apply + (columnWidths.slice cameraColumn)) width)
(controls.remove rightText))
(controls.add leftText)
(refreshColumnTexts)))
// Irreversibly disable the window's buttons (for when you're going to hide it in the next frame)
(method clearActions []
(_actions.clear))