mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-27 01:55:52 -05:00
Merge pull request #268 from FunkinCrew/feature/chart-editor-event-tooltips
Chart Editor: Tooltips when hovering over chart events
This commit is contained in:
commit
8b5f7a701d
5 changed files with 126 additions and 3 deletions
|
@ -15,7 +15,7 @@ abstract SongEventSchema(SongEventSchemaRaw)
|
|||
}
|
||||
|
||||
@:arrayAccess
|
||||
public inline function getByName(name:String):SongEventSchemaField
|
||||
public function getByName(name:String):SongEventSchemaField
|
||||
{
|
||||
for (field in this)
|
||||
{
|
||||
|
@ -41,6 +41,32 @@ abstract SongEventSchema(SongEventSchemaRaw)
|
|||
{
|
||||
return this[k] = v;
|
||||
}
|
||||
|
||||
public function stringifyFieldValue(name:String, value:Dynamic):String
|
||||
{
|
||||
var field:SongEventSchemaField = getByName(name);
|
||||
if (field == null) return 'Unknown';
|
||||
|
||||
switch (field.type)
|
||||
{
|
||||
case SongEventFieldType.STRING:
|
||||
return Std.string(value);
|
||||
case SongEventFieldType.INTEGER:
|
||||
return Std.string(value);
|
||||
case SongEventFieldType.FLOAT:
|
||||
return Std.string(value);
|
||||
case SongEventFieldType.BOOL:
|
||||
return Std.string(value);
|
||||
case SongEventFieldType.ENUM:
|
||||
for (key in field.keys.keys())
|
||||
{
|
||||
if (field.keys.get(key) == value) return key;
|
||||
}
|
||||
return Std.string(value);
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef SongEventSchemaRaw = Array<SongEventSchemaField>;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package funkin.data.song;
|
||||
|
||||
import funkin.data.event.SongEventRegistry;
|
||||
import funkin.play.event.SongEvent;
|
||||
import funkin.data.event.SongEventSchema;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import thx.semver.Version;
|
||||
|
@ -702,6 +703,11 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
|||
}
|
||||
}
|
||||
|
||||
public inline function getHandler():Null<SongEvent>
|
||||
{
|
||||
return SongEventRegistry.getEvent(this.event);
|
||||
}
|
||||
|
||||
public inline function getSchema():Null<SongEventSchema>
|
||||
{
|
||||
return SongEventRegistry.getEventSchema(this.event);
|
||||
|
@ -752,6 +758,39 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
|||
return this.value == null ? null : cast Reflect.field(this.value, key);
|
||||
}
|
||||
|
||||
public function buildTooltip():String
|
||||
{
|
||||
var eventHandler = getHandler();
|
||||
var eventSchema = getSchema();
|
||||
|
||||
if (eventSchema == null) return 'Unknown Event: ${this.event}';
|
||||
|
||||
var result = '${eventHandler.getTitle()}';
|
||||
|
||||
var defaultKey = eventSchema.getFirstField()?.name;
|
||||
var valueStruct:haxe.DynamicAccess<Dynamic> = valueAsStruct(defaultKey);
|
||||
|
||||
for (pair in valueStruct.keyValueIterator())
|
||||
{
|
||||
var key = pair.key;
|
||||
var value = pair.value;
|
||||
|
||||
var title = eventSchema.getByName(key)?.title ?? 'UnknownField';
|
||||
|
||||
if (eventSchema.stringifyFieldValue(key, value) != null) trace(eventSchema.stringifyFieldValue(key, value));
|
||||
var valueStr = eventSchema.stringifyFieldValue(key, value) ?? 'UnknownValue';
|
||||
|
||||
result += '\n- ${title}: ${valueStr}';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public function clone():SongEventData
|
||||
{
|
||||
return new SongEventData(this.time, this.event, this.value);
|
||||
}
|
||||
|
||||
@:op(A == B)
|
||||
public function op_equals(other:SongEventData):Bool
|
||||
{
|
||||
|
|
|
@ -2256,7 +2256,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
add(gridGhostHoldNote);
|
||||
gridGhostHoldNote.zIndex = 11;
|
||||
|
||||
gridGhostEvent = new ChartEditorEventSprite(this);
|
||||
gridGhostEvent = new ChartEditorEventSprite(this, true);
|
||||
gridGhostEvent.alpha = 0.6;
|
||||
gridGhostEvent.eventData = new SongEventData(-1, '', {});
|
||||
gridGhostEvent.visible = false;
|
||||
|
@ -3445,6 +3445,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Setting event data resets position relative to the grid so we fix that.
|
||||
eventSprite.x += renderedEvents.x;
|
||||
eventSprite.y += renderedEvents.y;
|
||||
eventSprite.updateTooltipPosition();
|
||||
}
|
||||
|
||||
// Add hold notes that have been made visible (but not their parents)
|
||||
|
|
|
@ -11,6 +11,9 @@ import flixel.graphics.frames.FlxFramesCollection;
|
|||
import flixel.graphics.frames.FlxTileFrames;
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.data.song.SongData.SongEventData;
|
||||
import haxe.ui.tooltips.ToolTipRegionOptions;
|
||||
import funkin.util.HaxeUIUtil;
|
||||
import haxe.ui.tooltips.ToolTipManager;
|
||||
|
||||
/**
|
||||
* A sprite that can be used to display a song event in a chart.
|
||||
|
@ -36,6 +39,13 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
|
||||
public var overrideStepTime(default, set):Null<Float> = null;
|
||||
|
||||
public var tooltip:ToolTipRegionOptions;
|
||||
|
||||
/**
|
||||
* Whether this sprite is a "ghost" sprite used when hovering to place a new event.
|
||||
*/
|
||||
public var isGhost:Bool = false;
|
||||
|
||||
function set_overrideStepTime(value:Null<Float>):Null<Float>
|
||||
{
|
||||
if (overrideStepTime == value) return overrideStepTime;
|
||||
|
@ -45,12 +55,14 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
return overrideStepTime;
|
||||
}
|
||||
|
||||
public function new(parent:ChartEditorState)
|
||||
public function new(parent:ChartEditorState, isGhost:Bool = false)
|
||||
{
|
||||
super();
|
||||
|
||||
this.parentState = parent;
|
||||
this.isGhost = isGhost;
|
||||
|
||||
this.tooltip = HaxeUIUtil.buildTooltip('N/A');
|
||||
this.frames = buildFrames();
|
||||
|
||||
buildAnimations();
|
||||
|
@ -142,6 +154,7 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
// Disown parent. MAKE SURE TO REVIVE BEFORE REUSING
|
||||
this.kill();
|
||||
this.visible = false;
|
||||
updateTooltipPosition();
|
||||
return null;
|
||||
}
|
||||
else
|
||||
|
@ -151,6 +164,8 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
this.eventData = value;
|
||||
// Update the position to match the note data.
|
||||
updateEventPosition();
|
||||
// Update the tooltip text.
|
||||
this.tooltip.tipData = {text: this.eventData.buildTooltip()};
|
||||
return this.eventData;
|
||||
}
|
||||
}
|
||||
|
@ -169,6 +184,31 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
this.x += origin.x;
|
||||
this.y += origin.y;
|
||||
}
|
||||
|
||||
this.updateTooltipPosition();
|
||||
}
|
||||
|
||||
public function updateTooltipPosition():Void
|
||||
{
|
||||
// No tooltip for ghost sprites.
|
||||
if (this.isGhost) return;
|
||||
|
||||
if (this.eventData == null)
|
||||
{
|
||||
// Disable the tooltip.
|
||||
ToolTipManager.instance.unregisterTooltipRegion(this.tooltip);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the position.
|
||||
this.tooltip.left = this.x;
|
||||
this.tooltip.top = this.y;
|
||||
this.tooltip.width = this.width;
|
||||
this.tooltip.height = this.height;
|
||||
|
||||
// Enable the tooltip.
|
||||
ToolTipManager.instance.registerTooltipRegion(this.tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
17
source/funkin/util/HaxeUIUtil.hx
Normal file
17
source/funkin/util/HaxeUIUtil.hx
Normal file
|
@ -0,0 +1,17 @@
|
|||
package funkin.util;
|
||||
|
||||
import haxe.ui.tooltips.ToolTipRegionOptions;
|
||||
|
||||
class HaxeUIUtil
|
||||
{
|
||||
public static function buildTooltip(text:String, ?left:Float, ?top:Float, ?width:Float, ?height:Float):ToolTipRegionOptions
|
||||
{
|
||||
return {
|
||||
tipData: {text: text},
|
||||
left: left ?? 0.0,
|
||||
top: top ?? 0.0,
|
||||
width: width ?? 0.0,
|
||||
height: height ?? 0.0
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue