mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-11-28 15:26:12 +00: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
|
|
@ -15,7 +15,7 @@ abstract SongEventSchema(SongEventSchemaRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
@:arrayAccess
|
@:arrayAccess
|
||||||
public inline function getByName(name:String):SongEventSchemaField
|
public function getByName(name:String):SongEventSchemaField
|
||||||
{
|
{
|
||||||
for (field in this)
|
for (field in this)
|
||||||
{
|
{
|
||||||
|
|
@ -41,6 +41,32 @@ abstract SongEventSchema(SongEventSchemaRaw)
|
||||||
{
|
{
|
||||||
return this[k] = v;
|
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>;
|
typedef SongEventSchemaRaw = Array<SongEventSchemaField>;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package funkin.data.song;
|
package funkin.data.song;
|
||||||
|
|
||||||
import funkin.data.event.SongEventRegistry;
|
import funkin.data.event.SongEventRegistry;
|
||||||
|
import funkin.play.event.SongEvent;
|
||||||
import funkin.data.event.SongEventSchema;
|
import funkin.data.event.SongEventSchema;
|
||||||
import funkin.data.song.SongRegistry;
|
import funkin.data.song.SongRegistry;
|
||||||
import thx.semver.Version;
|
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>
|
public inline function getSchema():Null<SongEventSchema>
|
||||||
{
|
{
|
||||||
return SongEventRegistry.getEventSchema(this.event);
|
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);
|
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)
|
@:op(A == B)
|
||||||
public function op_equals(other:SongEventData):Bool
|
public function op_equals(other:SongEventData):Bool
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2256,7 +2256,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
add(gridGhostHoldNote);
|
add(gridGhostHoldNote);
|
||||||
gridGhostHoldNote.zIndex = 11;
|
gridGhostHoldNote.zIndex = 11;
|
||||||
|
|
||||||
gridGhostEvent = new ChartEditorEventSprite(this);
|
gridGhostEvent = new ChartEditorEventSprite(this, true);
|
||||||
gridGhostEvent.alpha = 0.6;
|
gridGhostEvent.alpha = 0.6;
|
||||||
gridGhostEvent.eventData = new SongEventData(-1, '', {});
|
gridGhostEvent.eventData = new SongEventData(-1, '', {});
|
||||||
gridGhostEvent.visible = false;
|
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.
|
// Setting event data resets position relative to the grid so we fix that.
|
||||||
eventSprite.x += renderedEvents.x;
|
eventSprite.x += renderedEvents.x;
|
||||||
eventSprite.y += renderedEvents.y;
|
eventSprite.y += renderedEvents.y;
|
||||||
|
eventSprite.updateTooltipPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add hold notes that have been made visible (but not their parents)
|
// 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.graphics.frames.FlxTileFrames;
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
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.
|
* 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 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>
|
function set_overrideStepTime(value:Null<Float>):Null<Float>
|
||||||
{
|
{
|
||||||
if (overrideStepTime == value) return overrideStepTime;
|
if (overrideStepTime == value) return overrideStepTime;
|
||||||
|
|
@ -45,12 +55,14 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
return overrideStepTime;
|
return overrideStepTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function new(parent:ChartEditorState)
|
public function new(parent:ChartEditorState, isGhost:Bool = false)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.parentState = parent;
|
this.parentState = parent;
|
||||||
|
this.isGhost = isGhost;
|
||||||
|
|
||||||
|
this.tooltip = HaxeUIUtil.buildTooltip('N/A');
|
||||||
this.frames = buildFrames();
|
this.frames = buildFrames();
|
||||||
|
|
||||||
buildAnimations();
|
buildAnimations();
|
||||||
|
|
@ -142,6 +154,7 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
// Disown parent. MAKE SURE TO REVIVE BEFORE REUSING
|
// Disown parent. MAKE SURE TO REVIVE BEFORE REUSING
|
||||||
this.kill();
|
this.kill();
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
|
updateTooltipPosition();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -151,6 +164,8 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
this.eventData = value;
|
this.eventData = value;
|
||||||
// Update the position to match the note data.
|
// Update the position to match the note data.
|
||||||
updateEventPosition();
|
updateEventPosition();
|
||||||
|
// Update the tooltip text.
|
||||||
|
this.tooltip.tipData = {text: this.eventData.buildTooltip()};
|
||||||
return this.eventData;
|
return this.eventData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -169,6 +184,31 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
this.x += origin.x;
|
this.x += origin.x;
|
||||||
this.y += origin.y;
|
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