Compare commits

...

24 commits

Author SHA1 Message Date
lemz1
e9a1ee42b2 let the compiler figure out the type 2024-10-30 21:01:00 +01:00
lemz1
9ee4b0b94e no more checkstyle warnings 2024-10-30 21:01:00 +01:00
lemz1
907f300267 a bit nicer syntax 2024-10-30 21:01:00 +01:00
lemz1
ba1dbb5f68 all haxe abstracts 2024-10-30 21:01:00 +01:00
lemz1
d27433318d Update PolymodMacro.hx 2024-10-30 21:01:00 +01:00
lemz1
ee63e8d8d6 check for private abstracts 2024-10-30 21:01:00 +01:00
lemz1
ba72fee609 basically the most important abstracts 2024-10-30 21:01:00 +01:00
lemz1
42d93c616a lets make flxsignal an edge-case and use dynamic normally 2024-10-30 21:01:00 +01:00
lemz1
374f6837eb remove this useless vars 2024-10-30 21:01:00 +01:00
lemz1
f3b4cac92d make things dynamic (this is because typeparameters cause problems) 2024-10-30 21:01:00 +01:00
lemz1
918292d5a8 create abstracts for all specified modules 2024-10-30 21:01:00 +01:00
lemz1
07749285f1 flxsignal works now
i just need to make it work for all abstracts
2024-10-30 21:01:00 +01:00
lemz1
dcc58e9e33 polymod aliases 2024-10-30 21:01:00 +01:00
lemz1
6d35d07245 Update PolymodMacro.hx 2024-10-30 21:01:00 +01:00
lemz1
807373c2d2 lol 2024-10-30 21:01:00 +01:00
lemz1
c75112b998 build macro 2024-10-30 21:01:00 +01:00
lemz1
b26b1bf4e8 TODO: build specified abstracts 2024-10-30 21:01:00 +01:00
lemz1
0e36e9261d abstract without to and from should work now 2024-10-30 21:01:00 +01:00
lemz1
c00e4e8316 forgot that TType exists as well 2024-10-30 21:01:00 +01:00
lemz1
2d68f60c9b Update PolymodMacro.hx 2024-10-30 21:01:00 +01:00
lemz1
a01a5907e7 use module 2024-10-30 21:01:00 +01:00
lemz1
ad23a1c6d0 static member variables retrieved using getters 2024-10-30 21:01:00 +01:00
lemz1
d105686251 abstracts 2024-10-30 21:01:00 +01:00
Cameron Taylor
0d8e4a5330
fix: re-enable precise chart editor scrolling, and also fix smooth scroll playhead/playbar playback (#3806) 2024-10-30 12:41:28 -04:00
4 changed files with 446 additions and 4 deletions

View file

@ -588,6 +588,15 @@ class Project extends HXProject {
function configureCustomMacros() { function configureCustomMacros() {
// This macro allows addition of new functionality to existing Flixel. --> // This macro allows addition of new functionality to existing Flixel. -->
addHaxeMacro("addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')"); addHaxeMacro("addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')");
var abstracts:Array<String> = [
'.*',
'!thx.Set',
'!cpp.*',
'!lime.*',
'!openfl.*'
];
addHaxeMacro("funkin.util.macro.PolymodMacro.buildPolymodAbstracts(['" + abstracts.join("', '") + "'])");
} }
function configureOutputDir() { function configureOutputDir() {

View file

@ -248,6 +248,11 @@ class PolymodHandler
Polymod.addImportAlias('lime.utils.Assets', funkin.Assets); Polymod.addImportAlias('lime.utils.Assets', funkin.Assets);
Polymod.addImportAlias('openfl.utils.Assets', funkin.Assets); Polymod.addImportAlias('openfl.utils.Assets', funkin.Assets);
for (key => value in funkin.util.macro.PolymodMacro.aliases)
{
Polymod.addImportAlias(key, Type.resolveClass(value));
}
// Add blacklisting for prohibited classes and packages. // Add blacklisting for prohibited classes and packages.
// `Sys` // `Sys`
@ -333,8 +338,19 @@ class PolymodHandler
{ {
return { return {
assetLibraryPaths: [ assetLibraryPaths: [
'default' => 'preload', 'shared' => 'shared', 'songs' => 'songs', 'videos' => 'videos', 'tutorial' => 'tutorial', 'week1' => 'week1', 'default' => 'preload',
'week2' => 'week2', 'week3' => 'week3', 'week4' => 'week4', 'week5' => 'week5', 'week6' => 'week6', 'week7' => 'week7', 'weekend1' => 'weekend1', 'shared' => 'shared',
'songs' => 'songs',
'videos' => 'videos',
'tutorial' => 'tutorial',
'week1' => 'week1',
'week2' => 'week2',
'week3' => 'week3',
'week4' => 'week4',
'week5' => 'week5',
'week6' => 'week6',
'week7' => 'week7',
'weekend1' => 'weekend1',
], ],
coreAssetRedirect: CORE_FOLDER, coreAssetRedirect: CORE_FOLDER,
} }

View file

@ -2729,6 +2729,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
{ {
// Update the conductor and audio tracks to match. // Update the conductor and audio tracks to match.
currentScrollEase = d.value; currentScrollEase = d.value;
easeSongToScrollPosition(currentScrollEase);
} }
} }
@ -2741,7 +2742,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
playbarHeadDraggingWasPlaying = false; playbarHeadDraggingWasPlaying = false;
// Disabled code to resume song playback on drag. // Disabled code to resume song playback on drag.
// startAudioPlayback(); startAudioPlayback();
} }
} }
@ -3873,7 +3874,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
} }
// Mouse Wheel = Scroll // Mouse Wheel = Scroll
if (FlxG.mouse.wheel != 0 && !FlxG.keys.pressed.CONTROL) if (FlxG.mouse.wheel != 0)
{ {
scrollAmount = -50 * FlxG.mouse.wheel; scrollAmount = -50 * FlxG.mouse.wheel;
shouldPause = true; shouldPause = true;
@ -4469,6 +4470,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
0, songLengthInPixels); 0, songLengthInPixels);
currentScrollEase = clickedPosInPixels; currentScrollEase = clickedPosInPixels;
easeSongToScrollPosition(currentScrollEase);
} }
else if (scrollAnchorScreenPos != null) else if (scrollAnchorScreenPos != null)
{ {

View file

@ -0,0 +1,415 @@
package funkin.util.macro;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.ExprTools;
using StringTools;
/**
* This macro creates aliases for abstracts.
* That way we can use abstracts in hscript
*/
@SuppressWarnings(['checkstyle:CodeSimilarity', 'checkstyle:Dynamic'])
class PolymodMacro
{
/**
* The abstracts and their corresponding aliases
* `key` => original class path
* `value` => alias class path
*/
public static var aliases(get, never):Map<String, String>;
static function get_aliases():Map<String, String>
{
// truly a sight to behold
return Reflect.callMethod(null, Reflect.field(Type.resolveClass('funkin.util.macro.AbstractAliases'), 'get'), []);
}
/**
* Function that builds all the alias classes
* @param abstractClasses An array of packages and classes
* (`!` infront of the name will exclude classes from being built)
*/
public static macro function buildPolymodAbstracts(abstractClasses:Array<String>):Void
{
Context.onAfterTyping((types) -> {
if (alreadyCalled)
{
return;
}
var sortedAbstractClasses:Array<String> = [];
for (abstractCls in abstractClasses)
{
if (abstractCls.startsWith('!'))
{
sortedAbstractClasses.insert(0, abstractCls);
}
else
{
sortedAbstractClasses.push(abstractCls);
}
}
var abstractAliases:Map<String, String> = new Map<String, String>();
for (type in types)
{
switch (type)
{
case ModuleType.TAbstract(a):
var cls:AbstractType = a.get();
if (cls.isPrivate)
{
continue;
}
for (abstractCls in sortedAbstractClasses)
{
var negate:Bool = abstractCls.startsWith('!');
var name:String = abstractCls.replace('!', '').replace('.*', '');
if (!negate && !cls.module.startsWith(name) && moduleTypePath(cls) != name && packTypePath(cls) != name)
{
continue;
}
else if (negate)
{
if (cls.module.startsWith(name) || moduleTypePath(cls) == name || packTypePath(cls) == name)
{
break;
}
else
{
continue;
}
}
abstractAliases.set('${packTypePath(cls)}', 'polymod.abstracts.${packTypePath(cls)}${classSuffix}');
buildAbstract(cls);
break;
}
default:
// do nothing
}
}
Context.defineModule('funkin.util.macro.PolymodMacro', [
{
pack: ['funkin', 'util', 'macro'],
name: 'AbstractAliases',
kind: TypeDefKind.TDClass(null, [], false, false, false),
fields: [
{
name: 'get',
access: [Access.APublic, Access.AStatic],
kind: FieldType.FFun(
{
args: [],
ret: (macro :Map<String, String>),
expr: macro
{
return $v{abstractAliases};
}
}),
pos: Context.currentPos()
}
],
pos: Context.currentPos()
}
]);
// the callback is called twice, which this leads to issues
alreadyCalled = true;
});
}
#if macro
static var classSuffix:String = '_';
static var alreadyCalled:Bool = false;
static var skipFields:Array<String> = [];
static function buildAbstract(abstractCls:AbstractType):Void
{
if (abstractCls.impl == null)
{
return;
}
skipFields = [];
var cls:ClassType = abstractCls.impl.get();
// we use the functions to check whether we need to skip some fields
// that is why we sort the fields, so that functions are handled first
var sortedFields:Array<ClassField> = sortFields(cls.statics.get());
var fields:Array<Field> = [];
for (field in sortedFields)
{
if (field.name == '_new')
{
fields.push(buildCreateField(abstractCls, field));
continue;
}
fields = fields.concat(createFields(abstractCls, field));
};
Context.defineType(
{
pos: Context.currentPos(),
pack: ['polymod', 'abstracts'].concat(abstractCls.pack),
name: abstractCls.name + classSuffix,
kind: TypeDefKind.TDClass(null, [], false, false, false),
fields: fields,
}, null);
}
static function sortFields(fields:Array<ClassField>):Array<ClassField>
{
var sortedFields:Array<ClassField> = [];
for (field in fields)
{
switch (field.type)
{
case Type.TLazy(f):
switch (f())
{
case Type.TFun(_, _):
sortedFields.insert(0, field);
default:
sortedFields.push(field);
}
case Type.TFun(_, _):
sortedFields.insert(0, field);
default:
sortedFields.push(field);
}
}
return sortedFields;
}
static function buildCreateField(cls:AbstractType, field:ClassField):Field
{
var funcArgs:Array<FunctionArg> = [];
var funcArgNames:Array<String> = [];
switch (field.type)
{
case Type.TFun(args, _):
for (arg in args)
{
funcArgs.push(
{
name: arg.name,
type: null,
opt: arg.opt
});
funcArgNames.push(arg.name);
}
default:
throw 'how is this not a function';
}
var expr:String = '${newExpr(cls, field)}(${funcArgNames.join(', ')})';
return {
name: 'create',
access: [Access.APublic, Access.AStatic],
kind: FieldType.FFun(
{
args: funcArgs,
ret: null,
expr: macro
{
@:privateAccess
return ${Context.parse(expr, Context.currentPos())};
},
}),
pos: Context.currentPos()
};
}
static function newExpr(cls:AbstractType, field:ClassField):Null<String>
{
if ('${moduleTypePath(cls)}' == 'flixel.util.FlxSignal.FlxTypedSignal')
{
return 'new flixel.util.FlxSignal.FlxTypedSignal<Dynamic->Void>';
}
if (cls.params.length <= 0)
{
return 'new ${moduleTypePath(cls)}';
}
return 'new ${moduleTypePath(cls)}< ${[for (_ in 0...cls.params.length) 'Dynamic'].join(', ')} >';
}
static function createFields(cls:AbstractType, field:ClassField):Array<Field>
{
if (skipFields.contains(field.name))
{
return [];
}
switch (field.type)
{
case Type.TLazy(f):
return _createFields(cls, field, f());
default:
return _createFields(cls, field, field.type);
}
}
static function _createFields(cls:AbstractType, field:ClassField, type:Type):Array<Field>
{
if (field.meta.has(':to'))
{
return [];
}
var fields:Array<Field> = [];
switch (type)
{
case Type.TFun(args, ret):
var fieldArgs:Array<FunctionArg> = [];
var exprArgs:Array<String> = [];
for (arg in args)
{
if (arg.name == 'this')
{
var memberVariable:String = field.name.replace('get_', '').replace('set_', '');
if (memberVariable != field.name)
{
skipFields.push(memberVariable);
}
return [];
}
exprArgs.push(arg.name);
fieldArgs.push(
{
name: arg.name,
type: null,
opt: arg.opt,
});
}
var returnStr:String = switch (ret)
{
case Type.TAbstract(t, _):
if (t.get().name == 'Void')
{
'';
}
else
{
'return ';
}
default:
'return ';
};
var expr:String = '${returnStr}${moduleTypePath(cls)}.${field.name}(${exprArgs.join(', ')})';
fields.push(
{
name: field.name,
doc: field.doc,
access: [Access.APublic, Access.AStatic],
kind: FieldType.FFun(
{
args: fieldArgs,
ret: null,
expr: macro
{
@:privateAccess
${Context.parse(expr, Context.currentPos())};
},
params: []
}),
pos: Context.currentPos()
});
case Type.TAbstract(t, params):
fields.push(
{
name: field.name,
doc: field.doc,
access: [Access.APublic, Access.AStatic],
kind: FieldType.FProp('get', 'never', (macro :Dynamic), null),
pos: Context.currentPos()
});
var expr:String = '${moduleTypePath(cls)}.${field.name}';
fields.push(
{
name: 'get_${field.name}',
doc: field.doc,
access: [Access.APublic, Access.AStatic],
kind: FieldType.FFun(
{
args: [],
ret: null,
expr: macro
{
@:privateAccess
return ${Context.parse(expr, Context.currentPos())};
},
params: []
}),
pos: Context.currentPos()
});
case TType(t, params):
fields.push(
{
name: field.name,
doc: field.doc,
access: [Access.APublic, Access.AStatic],
kind: FieldType.FProp('get', 'never', (macro :Dynamic), null),
pos: Context.currentPos()
});
var expr:String = '${moduleTypePath(cls)}.${field.name}';
fields.push(
{
name: 'get_${field.name}',
doc: field.doc,
access: [Access.APublic, Access.AStatic],
kind: FieldType.FFun(
{
args: [],
ret: null,
expr: macro
{
@:privateAccess
return ${Context.parse(expr, Context.currentPos())};
},
params: []
}),
pos: Context.currentPos()
});
default:
return [];
};
return fields;
}
// Dynamic so any kind of type with `module` and `name` fields works
static function moduleTypePath(type:Dynamic):String
{
var dot:String = type.module.length != 0 ? '.' : '';
return '${type.module}${dot}${type.name}';
}
// Dynamic so any kind of type with `pack` and `name` fields works
static function packTypePath(type:Dynamic):String
{
var dot:String = type.pack.length != 0 ? '.' : '';
return '${type.pack.join('.')}${dot}${type.name}';
}
#end
}