Tobee_Night_Funkin_PSYCH/source/FlxUIDropDownMenuCustom.hx
2021-11-05 17:34:27 -04:00

618 lines
14 KiB
Haxe

package;
import flash.geom.Rectangle;
import flixel.addons.ui.interfaces.IFlxUIClickable;
import flixel.addons.ui.interfaces.IFlxUIWidget;
import flixel.addons.ui.interfaces.IHasParams;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.math.FlxMath;
import flixel.ui.FlxButton;
import flixel.util.FlxColor;
import flixel.util.FlxDestroyUtil;
import flixel.util.FlxStringUtil;
import flixel.addons.ui.FlxUIGroup;
import flixel.addons.ui.FlxUIText;
import flixel.addons.ui.FlxUIButton;
import flixel.addons.ui.FlxUISpriteButton;
import flixel.addons.ui.FlxUI9SliceSprite;
import flixel.addons.ui.FlxUIAssets;
import flixel.addons.ui.StrNameLabel;
import flixel.addons.ui.FlxUI;
/*
THIS IS AN EDIT OF FlxUIDropDownMenu I'VE MADE BECAUSE I'M TIRED OF IT NOT SUPPORTING SCROLLING UP/DOWN
BAH!
The differences are the following:
* Support to scrolling up/down with mouse wheel or arrow keys
* THe default drop direction is "Down" instead of "Automatic"
*/
/**
* @author larsiusprime
*/
class FlxUIDropDownMenuCustom extends FlxUIGroup implements IFlxUIWidget implements IFlxUIClickable implements IHasParams
{
public var skipButtonUpdate(default, set):Bool;
private function set_skipButtonUpdate(b:Bool):Bool
{
skipButtonUpdate = b;
header.button.skipButtonUpdate = b;
return b;
}
public var selectedId(get, set):String;
public var selectedLabel(get, set):String;
private var _selectedId:String;
private var _selectedLabel:String;
private var currentScroll:Int = 0; //Handles the scrolling
public var canScroll:Bool = true;
private function get_selectedId():String
{
return _selectedId;
}
private function set_selectedId(str:String):String
{
if (_selectedId == str)
return str;
var i:Int = 0;
for (btn in list)
{
if (btn != null && btn.name == str)
{
var item:FlxUIButton = list[i];
_selectedId = str;
if (item.label != null)
{
_selectedLabel = item.label.text;
header.text.text = item.label.text;
}
else
{
_selectedLabel = "";
header.text.text = "";
}
return str;
}
i++;
}
return str;
}
private function get_selectedLabel():String
{
return _selectedLabel;
}
private function set_selectedLabel(str:String):String
{
if (_selectedLabel == str)
return str;
var i:Int = 0;
for (btn in list)
{
if (btn.label.text == str)
{
var item:FlxUIButton = list[i];
_selectedId = item.name;
_selectedLabel = str;
header.text.text = str;
return str;
}
i++;
}
return str;
}
/**
* The header of this dropdown menu.
*/
public var header:FlxUIDropDownHeader;
/**
* The list of items that is shown when the toggle button is clicked.
*/
public var list:Array<FlxUIButton> = [];
/**
* The background for the list.
*/
public var dropPanel:FlxUI9SliceSprite;
public var params(default, set):Array<Dynamic>;
private function set_params(p:Array<Dynamic>):Array<Dynamic>
{
return params = p;
}
public var dropDirection(default, set):FlxUIDropDownMenuDropDirection = Down;
private function set_dropDirection(dropDirection):FlxUIDropDownMenuDropDirection
{
this.dropDirection = dropDirection;
updateButtonPositions();
return dropDirection;
}
public static inline var CLICK_EVENT:String = "click_dropdown";
public var callback:String->Void;
// private var _ui_control_callback:Bool->FlxUIDropDownMenuCustom->Void;
/**
* This creates a new dropdown menu.
*
* @param X x position of the dropdown menu
* @param Y y position of the dropdown menu
* @param DataList The data to be displayed
* @param Callback Optional Callback
* @param Header The header of this dropdown menu
* @param DropPanel Optional 9-slice-background for actual drop down menu
* @param ButtonList Optional list of buttons to be used for the corresponding entry in DataList
* @param UIControlCallback Used internally by FlxUI
*/
public function new(X:Float = 0, Y:Float = 0, DataList:Array<StrNameLabel>, ?Callback:String->Void, ?Header:FlxUIDropDownHeader,
?DropPanel:FlxUI9SliceSprite, ?ButtonList:Array<FlxUIButton>, ?UIControlCallback:Bool->FlxUIDropDownMenuCustom->Void)
{
super(X, Y);
callback = Callback;
header = Header;
dropPanel = DropPanel;
if (header == null)
header = new FlxUIDropDownHeader();
if (dropPanel == null)
{
var rect = new Rectangle(0, 0, header.background.width, header.background.height);
dropPanel = new FlxUI9SliceSprite(0, 0, FlxUIAssets.IMG_BOX, rect, [1, 1, 14, 14]);
}
if (DataList != null)
{
for (i in 0...DataList.length)
{
var data = DataList[i];
list.push(makeListButton(i, data.label, data.name));
}
selectSomething(DataList[0].name, DataList[0].label);
}
else if (ButtonList != null)
{
for (btn in ButtonList)
{
list.push(btn);
btn.resize(header.background.width, header.background.height);
btn.x = 1;
}
}
updateButtonPositions();
dropPanel.resize(header.background.width, getPanelHeight());
dropPanel.visible = false;
add(dropPanel);
for (btn in list)
{
add(btn);
btn.visible = false;
}
// _ui_control_callback = UIControlCallback;
header.button.onUp.callback = onDropdown;
add(header);
}
private function updateButtonPositions():Void
{
var buttonHeight = header.background.height;
dropPanel.y = header.background.y;
if (dropsUp())
dropPanel.y -= getPanelHeight();
else
dropPanel.y += buttonHeight;
var offset = dropPanel.y;
for (i in 0...currentScroll) { //Hides buttons that goes before the current scroll
var button:FlxUIButton = list[i];
if(button != null) {
button.y = -99999;
}
}
for (i in currentScroll...list.length)
{
var button:FlxUIButton = list[i];
if(button != null) {
button.y = offset;
offset += buttonHeight;
}
}
}
override function set_visible(Value:Bool):Bool
{
var vDropPanel = dropPanel.visible;
var vButtons = [];
for (i in 0...list.length)
{
if (list[i] != null)
{
vButtons.push(list[i].visible);
}
else
{
vButtons.push(false);
}
}
super.set_visible(Value);
dropPanel.visible = vDropPanel;
for (i in 0...list.length)
{
if (list[i] != null)
{
list[i].visible = vButtons[i];
}
}
return Value;
}
private function dropsUp():Bool
{
return dropDirection == Up || (dropDirection == Automatic && exceedsHeight());
}
private function exceedsHeight():Bool
{
return y + getPanelHeight() + header.background.height > FlxG.height;
}
private function getPanelHeight():Float
{
return list.length * header.background.height;
}
/**
* Change the contents with a new data list
* Replaces the old content with the new content
*/
public function setData(DataList:Array<StrNameLabel>):Void
{
var i:Int = 0;
if (DataList != null)
{
for (data in DataList)
{
var recycled:Bool = false;
if (list != null)
{
if (i <= list.length - 1)
{ // If buttons exist, try to re-use them
var btn:FlxUIButton = list[i];
if (btn != null)
{
btn.label.text = data.label; // Set the label
list[i].name = data.name; // Replace the name
recycled = true; // we successfully recycled it
}
}
}
else
{
list = [];
}
if (!recycled)
{ // If we couldn't recycle a button, make a fresh one
var t:FlxUIButton = makeListButton(i, data.label, data.name);
list.push(t);
add(t);
t.visible = false;
}
i++;
}
// Remove excess buttons:
if (list.length > DataList.length)
{ // we have more entries in the original set
for (j in DataList.length...list.length)
{ // start counting from end of list
var b:FlxUIButton = list.pop(); // remove last button on list
b.visible = false;
b.active = false;
remove(b, true); // remove from widget
b.destroy(); // destroy it
b = null;
}
}
selectSomething(DataList[0].name, DataList[0].label);
}
dropPanel.resize(header.background.width, getPanelHeight());
updateButtonPositions();
}
private function selectSomething(name:String, label:String):Void
{
header.text.text = label;
selectedId = name;
selectedLabel = label;
}
private function makeListButton(i:Int, Label:String, Name:String):FlxUIButton
{
var t:FlxUIButton = new FlxUIButton(0, 0, Label);
t.broadcastToFlxUI = false;
t.onUp.callback = onClickItem.bind(i);
t.name = Name;
t.loadGraphicSlice9([FlxUIAssets.IMG_INVIS, FlxUIAssets.IMG_HILIGHT, FlxUIAssets.IMG_HILIGHT], Std.int(header.background.width),
Std.int(header.background.height), [[1, 1, 3, 3], [1, 1, 3, 3], [1, 1, 3, 3]], FlxUI9SliceSprite.TILE_NONE);
t.labelOffsets[FlxButton.PRESSED].y -= 1; // turn off the 1-pixel depress on click
t.up_color = FlxColor.BLACK;
t.over_color = FlxColor.WHITE;
t.down_color = FlxColor.WHITE;
t.resize(header.background.width - 2, header.background.height - 1);
t.label.alignment = "left";
t.autoCenterLabel();
t.x = 1;
for (offset in t.labelOffsets)
{
offset.x += 2;
}
return t;
}
/*public function setUIControlCallback(UIControlCallback:Bool->FlxUIDropDownMenuCustom->Void):Void {
_ui_control_callback = UIControlCallback;
}*/
public function changeLabelByIndex(i:Int, NewLabel:String):Void
{
var btn:FlxUIButton = getBtnByIndex(i);
if (btn != null && btn.label != null)
{
btn.label.text = NewLabel;
}
}
public function changeLabelById(name:String, NewLabel:String):Void
{
var btn:FlxUIButton = getBtnById(name);
if (btn != null && btn.label != null)
{
btn.label.text = NewLabel;
}
}
public function getBtnByIndex(i:Int):FlxUIButton
{
if (i >= 0 && i < list.length)
{
return list[i];
}
return null;
}
public function getBtnById(name:String):FlxUIButton
{
for (btn in list)
{
if (btn.name == name)
{
return btn;
}
}
return null;
}
public override function update(elapsed:Float):Void
{
super.update(elapsed);
#if FLX_MOUSE
if (dropPanel.visible)
{
if(list.length > 1 && canScroll) {
if(FlxG.mouse.wheel > 0 || FlxG.keys.justPressed.UP) {
// Go up
--currentScroll;
if(currentScroll < 0) currentScroll = 0;
updateButtonPositions();
}
else if (FlxG.mouse.wheel < 0 || FlxG.keys.justPressed.DOWN) {
// Go down
currentScroll++;
if(currentScroll >= list.length) currentScroll = list.length-1;
updateButtonPositions();
}
}
if (FlxG.mouse.justPressed && !FlxG.mouse.overlaps(this))
{
showList(false);
}
}
#end
}
override public function destroy():Void
{
super.destroy();
dropPanel = FlxDestroyUtil.destroy(dropPanel);
list = FlxDestroyUtil.destroyArray(list);
// _ui_control_callback = null;
callback = null;
}
private function showList(b:Bool):Void
{
for (button in list)
{
button.visible = b;
button.active = b;
}
dropPanel.visible = b;
if(currentScroll != 0) {
currentScroll = 0;
updateButtonPositions();
}
FlxUI.forceFocus(b, this); // avoid overlaps
}
private function onDropdown():Void
{
(dropPanel.visible) ? showList(false) : showList(true);
}
private function onClickItem(i:Int):Void
{
var item:FlxUIButton = list[i];
selectSomething(item.name, item.label.text);
showList(false);
if (callback != null)
{
callback(item.name);
}
if (broadcastToFlxUI)
{
FlxUI.event(CLICK_EVENT, this, item.name, params);
}
}
/**
* Helper function to easily create a data list for a dropdown menu from an array of strings.
*
* @param StringArray The strings to use as data - used for both label and string ID.
* @param UseIndexID Whether to use the integer index of the current string as ID.
* @return The StrIDLabel array ready to be used in FlxUIDropDownMenuCustom's constructor
*/
public static function makeStrIdLabelArray(StringArray:Array<String>, UseIndexID:Bool = false):Array<StrNameLabel>
{
var strIdArray:Array<StrNameLabel> = [];
for (i in 0...StringArray.length)
{
var ID:String = StringArray[i];
if (UseIndexID)
{
ID = Std.string(i);
}
strIdArray[i] = new StrNameLabel(ID, StringArray[i]);
}
return strIdArray;
}
}
/**
* Header for a FlxUIDropDownMenuCustom
*/
class FlxUIDropDownHeader extends FlxUIGroup
{
/**
* The background of the header.
*/
public var background:FlxSprite;
/**
* The text that displays the currently selected item.
*/
public var text:FlxUIText;
/**
* The button that toggles the visibility of the dropdown panel.
*/
public var button:FlxUISpriteButton;
/**
* Creates a new dropdown header to be used in a FlxUIDropDownMenuCustom.
*
* @param Width Width of the dropdown - only relevant when no back sprite was specified
* @param Back Optional sprite to be placed in the background
* @param Text Optional text that displays the current value
* @param Button Optional button that toggles the dropdown list
*/
public function new(Width:Int = 120, ?Background:FlxSprite, ?Text:FlxUIText, ?Button:FlxUISpriteButton)
{
super();
background = Background;
text = Text;
button = Button;
// Background
if (background == null)
{
background = new FlxUI9SliceSprite(0, 0, FlxUIAssets.IMG_BOX, new Rectangle(0, 0, Width, 20), [1, 1, 14, 14]);
}
// Button
if (button == null)
{
button = new FlxUISpriteButton(0, 0, new FlxSprite(0, 0, FlxUIAssets.IMG_DROPDOWN));
button.loadGraphicSlice9([FlxUIAssets.IMG_BUTTON_THIN], 80, 20, [FlxStringUtil.toIntArray(FlxUIAssets.SLICE9_BUTTON)],
FlxUI9SliceSprite.TILE_NONE, -1, false, FlxUIAssets.IMG_BUTTON_SIZE, FlxUIAssets.IMG_BUTTON_SIZE);
}
button.resize(background.height, background.height);
button.x = background.x + background.width - button.width;
// Reposition and resize the button hitbox so the whole header is clickable
button.width = Width;
button.offset.x -= (Width - button.frameWidth);
button.x = offset.x;
button.label.offset.x += button.offset.x;
// Text
if (text == null)
{
text = new FlxUIText(0, 0, Std.int(background.width));
}
text.setPosition(2, 4);
text.color = FlxColor.BLACK;
add(background);
add(button);
add(text);
}
override public function destroy():Void
{
super.destroy();
background = FlxDestroyUtil.destroy(background);
text = FlxDestroyUtil.destroy(text);
button = FlxDestroyUtil.destroy(button);
}
}
enum FlxUIDropDownMenuDropDirection
{
Automatic;
Down;
Up;
}