WIP Slider
This commit is contained in:
@@ -528,7 +528,7 @@ final class CoolUtil
|
||||
case UNCHECKED: 'checkboxUnchecked';
|
||||
case WARNING: 'warningMenu';
|
||||
default: 'scroll';
|
||||
}), volume);
|
||||
}), volume * Options.volumeSFX);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,116 @@
|
||||
package funkin.menus.ui;
|
||||
|
||||
import flixel.animation.FlxAnimation;
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.math.FlxPoint;
|
||||
|
||||
class Slider extends FlxSprite {
|
||||
|
||||
public var playSound:Bool = true;
|
||||
public var selected:Bool = false;
|
||||
public var value:Float;
|
||||
public var segments:Int;
|
||||
|
||||
public var barWidth(default, set):Float;
|
||||
public var barHeight(default, null):Float;
|
||||
public var showSlider:Bool = true;
|
||||
|
||||
public var barFramerate:Float = 24.0;
|
||||
var __animTime:Float = 0.0;
|
||||
var __curSegments:Int;
|
||||
|
||||
public function new(?x:Float, ?y:Float, value:Float = 0.5, barWidth:Float = 500, segments:Int = 4) {
|
||||
super(x, y);
|
||||
this.value = value;
|
||||
this.barWidth = barWidth;
|
||||
this.segments = segments;
|
||||
|
||||
frames = Paths.getFrames('menus/options/slider');
|
||||
}
|
||||
|
||||
function playAnimOrDefault(a:String, d:String) {
|
||||
if (animation.exists(a)) animation.play(a);
|
||||
else animation.play(d);
|
||||
}
|
||||
|
||||
override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
__animTime += elapsed;
|
||||
|
||||
if (animation.curAnim == null) {
|
||||
animation.play(selected ? 'selected' : 'unselected');
|
||||
__curSegments = CoolUtil.minInt(segments, Math.floor(value * (segments + 1)));
|
||||
}
|
||||
else {
|
||||
switch (animation.curAnim.name) {
|
||||
case 'selected': if (!selected) playAnimOrDefault('deselect', 'unselected');
|
||||
case 'unselected': if (selected) playAnimOrDefault('selecting', 'selected');
|
||||
case 'selecting': if (animation.finished) animation.play('selected');
|
||||
case 'deselect': if (animation.finished) animation.play('unselected');
|
||||
}
|
||||
|
||||
if (__curSegments != (__curSegments = CoolUtil.minInt(segments, Math.floor(value * (segments + 1)))) && playSound) {
|
||||
FlxG.sound.play(Paths.sound('menu/volume')).pitch = 0.75 + __curSegments * 0.5 / (segments + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override function isSimpleRender(?camera:FlxCamera):Bool return false;
|
||||
override function drawComplex(camera:FlxCamera) {
|
||||
if (showSlider) {
|
||||
var xOff = frame.sourceSize.x * 0.5 - barWidth * value, yOff = (frame.sourceSize.y - barHeight) * 0.5;
|
||||
frameOffset.add(xOff, yOff);
|
||||
super.drawComplex(camera);
|
||||
frameOffset.subtract(xOff, yOff);
|
||||
}
|
||||
}
|
||||
|
||||
function getBarAnim(type:Int, filled:Bool):FlxAnimation {
|
||||
var name = (type == 1 ? 'corner ' : (type == 2 ? 'segment ' : 'bar ')) + (filled ? 'filled0' : 'empty0');
|
||||
if (animation.exists(name)) return animation.getByName(name);
|
||||
|
||||
animation.addByPrefix(name, name, barFramerate, true);
|
||||
return animation.getByName(name);
|
||||
}
|
||||
|
||||
inline function sliderResetFrameSize() {
|
||||
frameWidth = Std.int(barWidth);
|
||||
frameHeight = Std.int(barHeight);
|
||||
_halfSize.set(0.5 * frameWidth, 0.5 * frameHeight);
|
||||
resetSize();
|
||||
}
|
||||
|
||||
override function resetHelpers() {
|
||||
animation.addByPrefix('unselected', 'slider unselected0', 24, true);
|
||||
animation.addByPrefix('selected', 'slider selected0', 24, true);
|
||||
animation.addByPrefix('deselect', 'slider deselect0', 24, false);
|
||||
animation.addByPrefix('selecting', 'slider selecting0', 24, false);
|
||||
|
||||
var anim;
|
||||
barHeight = 0;
|
||||
for (type in 0...3) {
|
||||
if ((anim = getBarAnim(type, false)) != null) barHeight = Math.max(barHeight, frames.frames[anim.frames[0]].sourceSize.y);
|
||||
if ((anim = getBarAnim(type, true)) != null) barHeight = Math.max(barHeight, frames.frames[anim.frames[0]].sourceSize.y);
|
||||
}
|
||||
|
||||
sliderResetFrameSize();
|
||||
resetSizeFromFrame();
|
||||
centerOrigin();
|
||||
|
||||
if (FlxG.renderBlit) {
|
||||
dirty = true;
|
||||
updateFramePixels();
|
||||
}
|
||||
}
|
||||
|
||||
override function set_frame(v:FlxFrame):FlxFrame {
|
||||
super.set_frame(v);
|
||||
sliderResetFrameSize();
|
||||
return v;
|
||||
}
|
||||
|
||||
function set_barWidth(v:Float):Float {
|
||||
origin.x = _halfSize.x = 0.5 * (frameWidth = Std.int(barWidth = v));
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,14 @@ interface ITreeOption {
|
||||
var desc:String;
|
||||
var selected:Bool;
|
||||
|
||||
function reloadStrings():Void;
|
||||
function changeSelection(change:Int):Void;
|
||||
function select():Void;
|
||||
}
|
||||
|
||||
interface ITreeFloatOption extends ITreeOption {
|
||||
function changeValue(change:Float):Void;
|
||||
}
|
||||
|
||||
class TreeMenu extends UIState {
|
||||
public var onMenuClosed:FlxTypedSignal<TreeMenuScreen->Void> = new FlxTypedSignal();
|
||||
public var onMenuChanged:FlxTypedSignal<TreeMenuScreen->Void> = new FlxTypedSignal();
|
||||
|
||||
@@ -5,6 +5,8 @@ import flixel.util.FlxSignal;
|
||||
import funkin.backend.system.Controls;
|
||||
import funkin.backend.TurboControls;
|
||||
import funkin.options.TreeMenu.ITreeOption;
|
||||
import funkin.options.TreeMenu.ITreeFloatOption;
|
||||
import funkin.options.type.OptionType;
|
||||
import funkin.options.type.Separator;
|
||||
|
||||
class TreeMenuScreen extends FlxSpriteGroup {
|
||||
@@ -55,6 +57,7 @@ class TreeMenuScreen extends FlxSpriteGroup {
|
||||
var turboBasics:Array<TurboBasic>;
|
||||
|
||||
var curOption:ITreeOption;
|
||||
var curFloatOption:ITreeFloatOption;
|
||||
var __firstFrame:Bool = true;
|
||||
|
||||
public function new(name:String, desc:String, ?prefix:String, ?objects:Array<FlxSprite>) {
|
||||
@@ -72,7 +75,7 @@ class TreeMenuScreen extends FlxSpriteGroup {
|
||||
rawName = rawName;
|
||||
rawDesc = rawDesc;
|
||||
|
||||
for (object in members) if (object != null && object is ITreeOption) cast(object, ITreeOption).reloadStrings();
|
||||
for (object in members) if (object != null && object is OptionType) cast(object, OptionType).reloadStrings();
|
||||
}
|
||||
|
||||
override function update(elapsed:Float) {
|
||||
@@ -80,7 +83,10 @@ class TreeMenuScreen extends FlxSpriteGroup {
|
||||
|
||||
if (__firstFrame) {
|
||||
__firstFrame = false;
|
||||
if (members[curSelected] is ITreeOption) (curOption = cast members[curSelected]).selected = true;
|
||||
if (members[curSelected] is ITreeOption) {
|
||||
(curOption = cast members[curSelected]).selected = true;
|
||||
if (curOption is ITreeFloatOption) curFloatOption = cast curOption;
|
||||
}
|
||||
updateItems(true);
|
||||
return;
|
||||
}
|
||||
@@ -91,8 +97,14 @@ class TreeMenuScreen extends FlxSpriteGroup {
|
||||
|
||||
if (length > 0 && curOption != null) {
|
||||
if (controls.ACCEPT || (FlxG.mouse.justPressed && FlxG.mouse.overlaps(members[curSelected]))) curOption.select();
|
||||
if (leftTurboControl.activated) curOption.changeSelection(-1);
|
||||
if (rightTurboControl.activated) curOption.changeSelection(1);
|
||||
if (curFloatOption != null) {
|
||||
if (controls.LEFT) curFloatOption.changeValue(-elapsed);
|
||||
if (controls.RIGHT) curFloatOption.changeValue(elapsed);
|
||||
}
|
||||
else {
|
||||
if (leftTurboControl.activated) curOption.changeSelection(-1);
|
||||
if (rightTurboControl.activated) curOption.changeSelection(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (controls.BACK || (FlxG.mouse.justPressedRight && Main.timeSinceFocus > 0.3)) close();
|
||||
@@ -142,7 +154,15 @@ class TreeMenuScreen extends FlxSpriteGroup {
|
||||
if ((curSelected = FlxMath.wrap(curSelected + (change > 0 ? 1 : -1), 0, members.length - 1)) == prevSelect) break;
|
||||
|
||||
if (curOption != null) curOption.selected = false;
|
||||
if (members[curSelected] is ITreeOption) (curOption = cast members[curSelected]).selected = true;
|
||||
if (members[curSelected] is ITreeOption) {
|
||||
(curOption = cast members[curSelected]).selected = true;
|
||||
if (curOption is ITreeFloatOption) curFloatOption = cast curOption;
|
||||
else curFloatOption = null;
|
||||
}
|
||||
else {
|
||||
curOption = null;
|
||||
curFloatOption = null;
|
||||
}
|
||||
updateMenuDesc();
|
||||
|
||||
CoolUtil.playMenuSFX(SCROLL);
|
||||
|
||||
@@ -14,8 +14,9 @@ class GameplayOptions extends TreeMenuScreen {
|
||||
add(new Checkbox(getNameID('ghostTapping'), getDescID('ghostTapping'), 'ghostTapping'));
|
||||
add(new Checkbox(getNameID('naughtyness'), getDescID('naughtyness'), 'naughtyness'));
|
||||
add(new Checkbox(getNameID('camZoomOnBeat'), getDescID('camZoomOnBeat'), 'camZoomOnBeat'));
|
||||
add(new Checkbox(getNameID('autoPause'), getDescID('autoPause'), 'autoPause'));
|
||||
add(new Checkbox(getNameID('autoPause'), getDescID('autoPause'), 'autoPause', __changeAutoPause));
|
||||
add(offsetSetting = new NumOption(getNameID('songOffset'), getDescID('songOffset'), -999, 999, 1, 'songOffset', __changeOffset));
|
||||
add(new SliderOption(getNameID('volumeSFX'), getDescID('volumeSFX'), 0, 1, 1, 10, 'volumeSFX'));
|
||||
|
||||
add(new Separator());
|
||||
add(new TextOption('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc', ' >', () ->
|
||||
@@ -23,6 +24,7 @@ class GameplayOptions extends TreeMenuScreen {
|
||||
}
|
||||
|
||||
private function __changeOffset(offset:Float) Conductor.songOffset = offset;
|
||||
private function __changeAutoPause() FlxG.autoPause = Options.autoPause;
|
||||
|
||||
var __lastBeat:Int = 0;
|
||||
var __lastSongBeat:Int = 0;
|
||||
|
||||
@@ -10,7 +10,7 @@ class NumOption extends TextOption {
|
||||
public var max:Float;
|
||||
public var step:Float;
|
||||
|
||||
public var currentSelection:Float;
|
||||
public var currentValue:Float;
|
||||
|
||||
public var parent:Dynamic;
|
||||
public var optionName:String;
|
||||
@@ -31,20 +31,20 @@ class NumOption extends TextOption {
|
||||
this.optionName = optionName;
|
||||
this.parent = parent = parent != null ? parent : Options;
|
||||
|
||||
if (Reflect.field(parent, optionName) != null) currentSelection = Reflect.field(parent, optionName);
|
||||
if (Reflect.field(parent, optionName) != null) currentValue = Reflect.field(parent, optionName);
|
||||
|
||||
__number = new Alphabet(0, 20, ': $currentSelection', 'bold');
|
||||
__number = new Alphabet(0, 20, ': $currentValue', 'bold');
|
||||
super(text, desc);
|
||||
add(__number);
|
||||
}
|
||||
|
||||
override function changeSelection(change:Int):Void {
|
||||
if (locked) return;
|
||||
if (currentSelection == (currentSelection = FlxMath.bound(currentSelection + change, min, max))) return;
|
||||
__number.text = ': $currentSelection';
|
||||
if (currentValue == (currentValue = FlxMath.bound(currentValue + change, min, max))) return;
|
||||
__number.text = ': $currentValue';
|
||||
|
||||
Reflect.setField(parent, optionName, currentSelection);
|
||||
if (changedCallback != null) changedCallback(currentSelection);
|
||||
Reflect.setField(parent, optionName, currentValue);
|
||||
if (changedCallback != null) changedCallback(currentValue);
|
||||
|
||||
CoolUtil.playMenuSFX(SCROLL);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,58 @@
|
||||
package funkin.options.type;
|
||||
|
||||
class SliderOption extends OptionType {
|
||||
public var selectCallback:Float->Void;
|
||||
import funkin.menus.ui.Slider;
|
||||
import funkin.options.TreeMenu.ITreeFloatOption;
|
||||
|
||||
class SliderOption extends TextOption implements ITreeFloatOption {
|
||||
public var changedCallback:Float->Void;
|
||||
|
||||
public var min:Float;
|
||||
public var max:Float;
|
||||
public var segmentVal:Float;
|
||||
public var step:Float;
|
||||
|
||||
public var currentSelection:Float;
|
||||
public var currentValue:Float;
|
||||
|
||||
public var parent:Dynamic;
|
||||
public var optionName:String;
|
||||
|
||||
//public var barWidth(get, set):Int;
|
||||
public var slider:Slider;
|
||||
|
||||
override function set_text(v:String) return __text.text = text = v;
|
||||
function getValue():Float return (currentValue - min) / (max - min);
|
||||
|
||||
var __text:Alphabet;
|
||||
var optionName:String;
|
||||
|
||||
public function new(text:String, desc:String, min:Float, max:Float, segmentVal:Float, optionName:String, barWidth:Int = 520,
|
||||
?selectCallback:Float->Void = null, ?parent:Dynamic)
|
||||
public function new(text:String, desc:String, min:Float, max:Float, step:Float, ?segments:Int, optionName:String, barWidth = 600.0,
|
||||
?changedCallback:Float->Void = null, ?parent:Dynamic)
|
||||
{
|
||||
super(text, desc);
|
||||
this.changedCallback = changedCallback;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.step = step;
|
||||
this.optionName = optionName;
|
||||
this.parent = parent = parent != null ? parent : Options;
|
||||
|
||||
if (Reflect.field(parent, optionName) != null) currentValue = Reflect.field(parent, optionName);
|
||||
|
||||
add(slider = new Slider(16, 0, getValue(), barWidth, segments));
|
||||
slider.scale.set(0.75, 0.75);
|
||||
slider.updateHitbox();
|
||||
slider.y = __text.y + (__text.height - slider.height) * 0.5;
|
||||
}
|
||||
|
||||
override function update(elapsed:Float) {
|
||||
slider.selected = selected && !locked;
|
||||
slider.value = getValue();
|
||||
__text.x = slider.x + slider.width + 30;
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
public function changeValue(change:Float):Void {
|
||||
if (locked) return;
|
||||
if (currentValue == (currentValue = FlxMath.bound(currentValue + change * step, min, max))) return;
|
||||
|
||||
Reflect.setField(parent, optionName, currentValue);
|
||||
if (changedCallback != null) changedCallback(currentValue);
|
||||
}
|
||||
|
||||
override function select() {}
|
||||
}
|
||||
Reference in New Issue
Block a user