move sources to src/main
This commit is contained in:
73
src/main/CompilationOption.hx
Executable file
73
src/main/CompilationOption.hx
Executable file
@@ -0,0 +1,73 @@
|
||||
package;
|
||||
#if macro
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
#end
|
||||
|
||||
/**
|
||||
* CompilationOption allows us to use compile-time variables for advanced conditional compilation.
|
||||
*/
|
||||
class CompilationOption {
|
||||
#if macro
|
||||
/**
|
||||
* Internal storage of the options.
|
||||
*/
|
||||
static var storage = new Map<String,Dynamic>();
|
||||
#end
|
||||
|
||||
/**
|
||||
* Set `key` to `value`.
|
||||
*
|
||||
* For simplicity `value` can only be constant Bool/Int/Float/String or null.
|
||||
* Array and structures are also possible, check:
|
||||
* http://haxe.org/manual/macros#constant-arguments
|
||||
*
|
||||
* Set `force` to true in order to override an option.
|
||||
* But be careful overriding will influenced by compilation order which may be hard to predict.
|
||||
*/
|
||||
@:overload(function (key:String, value:Bool, ?force:Bool = false):Void{})
|
||||
@:overload(function (key:String, value:Int, ?force:Bool = false):Void{})
|
||||
@:overload(function (key:String, value:Float, ?force:Bool = false):Void{})
|
||||
@:overload(function (key:String, value:String, ?force:Bool = false):Void{})
|
||||
macro static public function set(key:String, value:Dynamic, ?force:Bool = false) {
|
||||
if (!force && storage.exists(key))
|
||||
throw key + " has already been set to " + storage.get(key);
|
||||
|
||||
storage.set(key, value);
|
||||
|
||||
return macro {}; //an empty block, which means nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the option as a constant.
|
||||
*/
|
||||
macro static public function get(key:String):Expr {
|
||||
return Context.makeExpr(storage.get(key), Context.currentPos());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if `key` was set.
|
||||
*/
|
||||
macro static public function exists(key:String):ExprOf<Bool> {
|
||||
return Context.makeExpr(storage.exists(key), Context.currentPos());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an option. Returns true if there was such option.
|
||||
*/
|
||||
macro static public function remove(key:String):ExprOf<Bool> {
|
||||
return Context.makeExpr(storage.remove(key), Context.currentPos());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the options as an object with keys as fields.
|
||||
* eg. trace(CompilationOption.dump());
|
||||
*/
|
||||
macro static public function dump():ExprOf<Dynamic> {
|
||||
var obj = {};
|
||||
for (key in storage.keys()) {
|
||||
Reflect.setField(obj, key, storage.get(key));
|
||||
}
|
||||
return Context.makeExpr(obj, Context.currentPos());
|
||||
}
|
||||
}
|
||||
28
src/main/L.hx
Executable file
28
src/main/L.hx
Executable file
@@ -0,0 +1,28 @@
|
||||
package;
|
||||
|
||||
import haxework.log.ILogger;
|
||||
|
||||
class L {
|
||||
|
||||
private static var loggers:Array<ILogger> = new Array<ILogger>();
|
||||
|
||||
public static function push(logger:ILogger):Void {
|
||||
loggers.push(logger);
|
||||
}
|
||||
|
||||
public static function d(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
|
||||
for (logger in loggers) logger.d(tag, message, error, p);
|
||||
}
|
||||
|
||||
public static function i(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
|
||||
for (logger in loggers) logger.i(tag, message, error, p);
|
||||
}
|
||||
|
||||
public static function w(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
|
||||
for (logger in loggers) logger.w(tag, message, error, p);
|
||||
}
|
||||
|
||||
public static function e(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
|
||||
for (logger in loggers) logger.e(tag, message, error, p);
|
||||
}
|
||||
}
|
||||
30
src/main/Meta.hx
Executable file
30
src/main/Meta.hx
Executable file
@@ -0,0 +1,30 @@
|
||||
package;
|
||||
|
||||
#if macro
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
#end
|
||||
|
||||
class Meta {
|
||||
|
||||
#if macro
|
||||
private static inline var VERSION:String = "version";
|
||||
private static inline var BUILD:String = "build";
|
||||
|
||||
private static var data:Map<String, Dynamic> = new Map<String, Dynamic>();
|
||||
#end
|
||||
|
||||
macro static public function getBuild():ExprOf<String> {
|
||||
return Context.makeExpr(data.get(BUILD), Context.currentPos());
|
||||
}
|
||||
|
||||
macro static public function getVersion():ExprOf<String> {
|
||||
return Context.makeExpr(data.get(VERSION), Context.currentPos());
|
||||
}
|
||||
|
||||
macro static public function set(version:String) {
|
||||
data.set(BUILD, Date.now().toString());
|
||||
data.set(VERSION, version);
|
||||
return macro {};
|
||||
}
|
||||
}
|
||||
53
src/main/haxework/animate/Animate.hx
Normal file
53
src/main/haxework/animate/Animate.hx
Normal file
@@ -0,0 +1,53 @@
|
||||
package haxework.animate;
|
||||
|
||||
import openfl.events.Event;
|
||||
import flash.display.Stage;
|
||||
|
||||
class Animate implements IAnimate {
|
||||
|
||||
private static var running:Array<IAnimate> = new Array<IAnimate>();
|
||||
|
||||
public static function bind(stage:Stage):Void {
|
||||
stage.addEventListener(Event.ENTER_FRAME, function(_) {
|
||||
Animate.updateAll();
|
||||
});
|
||||
}
|
||||
|
||||
public static function updateAll():Void {
|
||||
if (running.length > 0) {
|
||||
var time = Date.now().getTime();
|
||||
for (animate in running) animate.update(time);
|
||||
}
|
||||
}
|
||||
|
||||
private var callback:Animate -> Void;
|
||||
private var duration:Int;
|
||||
private var startTime:Float;
|
||||
private var progress:Float;
|
||||
|
||||
public function new(duration:Int) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public function start(callback:IAnimate -> Void, ?custom:Bool = false):Void {
|
||||
startTime = Date.now().getTime();
|
||||
this.callback = callback;
|
||||
if (!custom) running.push(this);
|
||||
update(startTime);
|
||||
}
|
||||
|
||||
private function update(time:Float):Void {
|
||||
progress = (time - startTime) / duration;
|
||||
if (progress >= 1) {
|
||||
running.remove(this);
|
||||
if (callback != null) {
|
||||
callback(this);
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function cancel():Void {
|
||||
if (!Math.isNaN(startTime)) update(startTime + duration);
|
||||
}
|
||||
}
|
||||
63
src/main/haxework/animate/CircleMaskAnimate.hx
Executable file
63
src/main/haxework/animate/CircleMaskAnimate.hx
Executable file
@@ -0,0 +1,63 @@
|
||||
package haxework.animate;
|
||||
|
||||
import flash.display.DisplayObjectContainer;
|
||||
import haxework.animate.IAnimate;
|
||||
import flash.display.Sprite;
|
||||
import haxework.gui.IView;
|
||||
import haxework.animate.Animate;
|
||||
|
||||
class CircleMaskAnimate extends Animate {
|
||||
|
||||
private var view:IView;
|
||||
private var mask:Sprite;
|
||||
private var cyrcle:Sprite;
|
||||
private var size:Float;
|
||||
|
||||
public function new(view:IView, ?duration:Int = 1000) {
|
||||
super(duration);
|
||||
this.view = view;
|
||||
this.mask = new Sprite();
|
||||
this.cyrcle = new Sprite();
|
||||
}
|
||||
|
||||
override public function start(callback:IAnimate -> Void, ?custom:Bool = false):Void {
|
||||
var width = view.parent.width;
|
||||
var height = view.parent.height;
|
||||
size = Math.sqrt(width * width + height * height);
|
||||
//size = Math.max(width, height);
|
||||
cyrcle.x = mask.x = -(size - width) / 2 - size;
|
||||
cyrcle.y = mask.y = -(size - height) / 2 - size;
|
||||
|
||||
redraw(size, size);
|
||||
|
||||
view.parent.container.addChild(mask);
|
||||
view.content.mask = mask;
|
||||
view.parent.container.addChild(cyrcle);
|
||||
|
||||
super.start(callback, custom);
|
||||
}
|
||||
|
||||
private function redraw(size:Float, r:Float):Void {
|
||||
mask.graphics.clear();
|
||||
mask.graphics.beginFill(0xffffff);
|
||||
mask.graphics.drawCircle(size + size / 2, size + size / 2, r / 2);
|
||||
mask.graphics.endFill();
|
||||
|
||||
cyrcle.graphics.clear();
|
||||
cyrcle.graphics.lineStyle(8, 0xffffff);
|
||||
cyrcle.graphics.drawCircle(size + size / 2, size + size / 2, r / 2);
|
||||
cyrcle.graphics.lineStyle();
|
||||
}
|
||||
|
||||
override private function update(time:Float):Void {
|
||||
super.update(time);
|
||||
|
||||
redraw(size, size * progress);
|
||||
|
||||
if (progress >= 1 && view.content.parent != null) {
|
||||
if (view.content.parent.contains(mask)) view.content.parent.removeChild(mask);
|
||||
view.content.mask = null;
|
||||
if (view.content.parent.contains(cyrcle)) view.parent.container.removeChild(cyrcle);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/main/haxework/animate/FadeAnimate.hx
Executable file
29
src/main/haxework/animate/FadeAnimate.hx
Executable file
@@ -0,0 +1,29 @@
|
||||
package haxework.animate;
|
||||
|
||||
import haxework.animate.IAnimate;
|
||||
import flash.display.Sprite;
|
||||
import haxework.gui.IView;
|
||||
import haxework.animate.Animate;
|
||||
|
||||
class FadeAnimate extends Animate {
|
||||
|
||||
private var view:IView;
|
||||
|
||||
public function new(view:IView, ?duration = 500) {
|
||||
super(duration);
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
override public function start(callback:IAnimate -> Void, ?custom:Bool = false):Void {
|
||||
view.content.alpha = 1.0;
|
||||
super.start(callback, custom);
|
||||
}
|
||||
|
||||
override private function update(time:Float):Void {
|
||||
super.update(time);
|
||||
view.content.alpha = 1 - (progress * 1.0);
|
||||
if (progress >= 1) {
|
||||
view.content.alpha = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/main/haxework/animate/IAnimate.hx
Normal file
9
src/main/haxework/animate/IAnimate.hx
Normal file
@@ -0,0 +1,9 @@
|
||||
package haxework.animate;
|
||||
|
||||
interface IAnimate {
|
||||
|
||||
public function start(callback:IAnimate->Void, ?custom:Bool = false):Void;
|
||||
public function cancel():Void;
|
||||
|
||||
private function update(time:Float):Void;
|
||||
}
|
||||
28
src/main/haxework/animate/UnFadeAnimate.hx
Executable file
28
src/main/haxework/animate/UnFadeAnimate.hx
Executable file
@@ -0,0 +1,28 @@
|
||||
package haxework.animate;
|
||||
|
||||
import flash.display.Sprite;
|
||||
import haxework.gui.IView;
|
||||
import haxework.animate.Animate;
|
||||
|
||||
class UnFadeAnimate extends Animate {
|
||||
|
||||
private var view:IView;
|
||||
|
||||
public function new(view:IView, ?duration = 500) {
|
||||
super(duration);
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
override public function start(callback:IAnimate -> Void, ?custom:Bool = false):Void {
|
||||
view.content.alpha = 0.0;
|
||||
super.start(callback, custom);
|
||||
}
|
||||
|
||||
override private function update(time:Float):Void {
|
||||
super.update(time);
|
||||
view.content.alpha = progress * 1.0;
|
||||
if (progress >= 1) {
|
||||
view.content.alpha = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/main/haxework/asset/JsonAsset.hx
Executable file
14
src/main/haxework/asset/JsonAsset.hx
Executable file
@@ -0,0 +1,14 @@
|
||||
package haxework.asset;
|
||||
|
||||
import haxe.Json;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
class JsonAsset extends ByteArray {
|
||||
|
||||
public var value(default, null):Dynamic;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
value = Json.parse(readUTFBytes(bytesAvailable));
|
||||
}
|
||||
}
|
||||
13
src/main/haxework/asset/StringAsset.hx
Executable file
13
src/main/haxework/asset/StringAsset.hx
Executable file
@@ -0,0 +1,13 @@
|
||||
package haxework.asset;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
class StringAsset extends ByteArray {
|
||||
|
||||
public var value(default, null):String;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
value = readUTFBytes(bytesAvailable);
|
||||
}
|
||||
}
|
||||
15
src/main/haxework/core/Const.hx
Executable file
15
src/main/haxework/core/Const.hx
Executable file
@@ -0,0 +1,15 @@
|
||||
package haxework.core;
|
||||
|
||||
class Const {
|
||||
#if flash
|
||||
public static var UINT_MAX_VALUE:UInt = untyped __global__["uint"].MAX_VALUE;
|
||||
public static var UINT_MIN_VALUE:UInt = untyped __global__["uint"].MIN_VALUE;
|
||||
public static var INT_MAX_VALUE:Int = untyped __global__["int"].MAX_VALUE;
|
||||
public static var INT_MIN_VALUE:Int = untyped __global__["int"].MIN_VALUE;
|
||||
#else
|
||||
public static var UINT_MAX_VALUE:UInt = 0xffffffff;
|
||||
public static var UINT_MIN_VALUE:UInt = 0;
|
||||
public static var INT_MAX_VALUE:Int = 0xffffffff;
|
||||
public static var INT_MIN_VALUE:Int = -0xffffffff;
|
||||
#end
|
||||
}
|
||||
5
src/main/haxework/core/IDisposable.hx
Executable file
5
src/main/haxework/core/IDisposable.hx
Executable file
@@ -0,0 +1,5 @@
|
||||
package haxework.core;
|
||||
|
||||
interface IDisposable {
|
||||
public function dispose():Void;
|
||||
}
|
||||
31
src/main/haxework/core/Set.hx
Executable file
31
src/main/haxework/core/Set.hx
Executable file
@@ -0,0 +1,31 @@
|
||||
package haxework.core;
|
||||
|
||||
import haxe.ds.ObjectMap;
|
||||
import Map.IMap;
|
||||
|
||||
class Set<T:{}> {
|
||||
|
||||
private static var O:Dynamic = {};
|
||||
|
||||
private var map:ObjectMap<T, Dynamic>;
|
||||
|
||||
public function new() {
|
||||
map = new ObjectMap<T, Dynamic>();
|
||||
}
|
||||
|
||||
public inline function iterator():Iterator<T> {
|
||||
return map.keys();
|
||||
}
|
||||
|
||||
public inline function set(value:T):Void {
|
||||
map.set(value, O);
|
||||
}
|
||||
|
||||
public inline function exists(value:T):Bool {
|
||||
return map.exists(value);
|
||||
}
|
||||
|
||||
public inline inline function remove(value:T):Bool {
|
||||
return map.remove(value);
|
||||
}
|
||||
}
|
||||
133
src/main/haxework/core/Tuple.hx
Executable file
133
src/main/haxework/core/Tuple.hx
Executable file
@@ -0,0 +1,133 @@
|
||||
package haxework.core;
|
||||
typedef Tuple2#if!H<T1, T2>#end = {
|
||||
var first(default, null):T1;
|
||||
var second(default, null):T2;
|
||||
}
|
||||
typedef Tuple3#if!H<T1, T2, T3>#end = {> Tuple2<T1, T2>,
|
||||
var third(default, null):T3;
|
||||
}
|
||||
typedef Tuple4#if!H<T1, T2, T3, T4>#end = {> Tuple3<T1, T2, T3>,
|
||||
var fourth(default, null):T4;
|
||||
}
|
||||
typedef Tuple5#if!H<T1, T2, T3, T4, T5>#end = {> Tuple4<T1, T2, T3, T4>,
|
||||
var fifth(default, null):T5;
|
||||
}
|
||||
|
||||
class Tuple {
|
||||
public static function five<T1, T2, T3, T4, T5>(first:T1, second:T2, third:T3, fourth:T4, fifth:T5):Tuple5<T1, T2, T3, T4, T5> {
|
||||
return new InternalTuple5(first, second, third, fourth, fifth);
|
||||
}
|
||||
|
||||
public static function four<T1, T2, T3, T4>(first:T1, second:T2, third:T3, fourth:T4):Tuple4<T1, T2, T3, T4> {
|
||||
return new InternalTuple4(first, second, third, fourth);
|
||||
}
|
||||
|
||||
public static function three<T1, T2, T3>(first:T1, second:T2, third:T3):Tuple3<T1, T2, T3> {
|
||||
return new InternalTuple3(first, second, third);
|
||||
}
|
||||
|
||||
public static function two<T1, T2>(first:T1, second:T2):Tuple2<T1, T2> {
|
||||
return new InternalTuple2(first, second);
|
||||
}
|
||||
|
||||
public static inline function asTuple2<T1, T2, T3>(tuple:Tuple3<T1, T2, T3>):Tuple2<T1, T2>
|
||||
return tuple;
|
||||
|
||||
public static inline function asTuple3<T1, T2, T3, T4>(tuple:Tuple4<T1, T2, T3, T4>):Tuple3<T1, T2, T3>
|
||||
return tuple;
|
||||
|
||||
public static inline function asTuple4<T1, T2, T3, T4, T5>(tuple:Tuple5<T1, T2, T3, T4, T5>):Tuple4<T1, T2, T3, T4>
|
||||
return tuple;
|
||||
}
|
||||
|
||||
private class InternalTuple2<T1, T2> {
|
||||
public var first(default, null):T1;
|
||||
public var second(default, null):T2;
|
||||
|
||||
/**
|
||||
* Creates a new tuple.
|
||||
* @param first The first value.
|
||||
* @param second The second value.
|
||||
*/
|
||||
|
||||
public function new(first:T1, second:T2) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return "(" + first + ", " + second + ")";
|
||||
}
|
||||
}
|
||||
private class InternalTuple3<T1, T2, T3> extends InternalTuple2<T1, T2> {
|
||||
public var third(default, null):T3;
|
||||
|
||||
/**
|
||||
* Creates a new tuple.
|
||||
* @param first The first value.
|
||||
* @param second The second value.
|
||||
* @param third The third value.
|
||||
*/
|
||||
|
||||
public function new(first:T1, second:T2, third:T3) {
|
||||
super(first, second);
|
||||
this.third = third;
|
||||
}
|
||||
|
||||
public override function toString():String {
|
||||
return "("
|
||||
+ first + ", "
|
||||
+ second + ", "
|
||||
+ third + ")";
|
||||
}
|
||||
}
|
||||
private class InternalTuple4<T1, T2, T3, T4> extends InternalTuple3<T1, T2, T3> {
|
||||
public var fourth(default, null):T4;
|
||||
|
||||
/**
|
||||
* Creates a new tuple.
|
||||
* @param first The first value.
|
||||
* @param second The second value.
|
||||
* @param third The third value.
|
||||
* @param fourth The fourth value.
|
||||
*/
|
||||
|
||||
public function new(first:T1, second:T2, third:T3, fourth:T4) {
|
||||
super(first, second, third);
|
||||
this.fourth = fourth;
|
||||
}
|
||||
|
||||
public override function toString():String {
|
||||
return "("
|
||||
+ first + ", "
|
||||
+ second + ", "
|
||||
+ third + ", "
|
||||
+ fourth + ")";
|
||||
}
|
||||
}
|
||||
private class InternalTuple5<T1, T2, T3, T4, T5> extends InternalTuple4<T1, T2, T3, T4> {
|
||||
public var fifth(default, null):T5;
|
||||
|
||||
/**
|
||||
* Creates a new tuple.
|
||||
* @param first The first value.
|
||||
* @param second The second value.
|
||||
* @param third The third value.
|
||||
* @param fourth The fourth value.
|
||||
* @param fifth The fifth value.
|
||||
*/
|
||||
|
||||
public function new(first:T1, second:T2, third:T3, fourth:T4, fifth:T5) {
|
||||
super(first, second, third, fourth);
|
||||
this.fifth = fifth;
|
||||
}
|
||||
|
||||
public override function toString():String {
|
||||
return "("
|
||||
+ first + ", "
|
||||
+ second + ", "
|
||||
+ third + ", "
|
||||
+ fourth + ", "
|
||||
+ fifth + ")";
|
||||
}
|
||||
}
|
||||
36
src/main/haxework/dispath/Dispatcher.hx
Executable file
36
src/main/haxework/dispath/Dispatcher.hx
Executable file
@@ -0,0 +1,36 @@
|
||||
package haxework.dispath;
|
||||
|
||||
import haxe.ds.ObjectMap;
|
||||
|
||||
class Dispatcher<L:{}> implements IDispatcher<L> {
|
||||
|
||||
private var listeners(null, null):ObjectMap<L, Bool>;
|
||||
|
||||
public function new() {
|
||||
listeners = new ObjectMap<L, Bool>();
|
||||
}
|
||||
|
||||
public function addListener(listener:L, once:Bool = false):Void {
|
||||
listeners.set(listener, once);
|
||||
}
|
||||
|
||||
public function removeListener(listener:L):Bool {
|
||||
return listeners.remove(listener);
|
||||
}
|
||||
|
||||
public function removeAllListeners():Void {
|
||||
var i:Iterator<L> = listeners.keys();
|
||||
while (i.hasNext()) listeners.remove(i.next());
|
||||
}
|
||||
|
||||
public function dispatch(caller:L->Void):Void {
|
||||
var i:Iterator<L> = listeners.keys();
|
||||
var r:Array<L> = [];
|
||||
while (i.hasNext()) {
|
||||
var l = i.next();
|
||||
caller(l);
|
||||
if (listeners.get(l)) r.push(l);
|
||||
};
|
||||
for (l in r) listeners.remove(l);
|
||||
}
|
||||
}
|
||||
13
src/main/haxework/dispath/IDispatcher.hx
Executable file
13
src/main/haxework/dispath/IDispatcher.hx
Executable file
@@ -0,0 +1,13 @@
|
||||
package haxework.dispath;
|
||||
|
||||
import haxe.ds.ObjectMap;
|
||||
|
||||
interface IDispatcher<L:{}> {
|
||||
|
||||
private var listeners(null, null):ObjectMap<L, Bool>;
|
||||
|
||||
public function addListener(listener:L, once:Bool = false):Void;
|
||||
public function removeListener(listener:L):Bool;
|
||||
public function removeAllListeners():Void;
|
||||
public function dispatch(caller:L->Void):Void;
|
||||
}
|
||||
47
src/main/haxework/format/Formatter.hx
Executable file
47
src/main/haxework/format/Formatter.hx
Executable file
@@ -0,0 +1,47 @@
|
||||
package haxework.format;
|
||||
|
||||
import haxework.provider.Provider;
|
||||
import haxework.locale.ILocale;
|
||||
|
||||
class Formatter implements IFormatter {
|
||||
|
||||
public function formatDate(date:Date, format:String):String {
|
||||
var locale:ILocale = Provider.get(ILocale);
|
||||
var r:EReg = ~/\{(\w+)\}/g;
|
||||
return r.map(format, function(r:EReg):String {
|
||||
return switch(r.matched(1)) {
|
||||
case "ss": doubleDigit(date.getSeconds());
|
||||
case "nn": doubleDigit(date.getMinutes());
|
||||
case "hh": doubleDigit(date.getHours());
|
||||
case "dd": doubleDigit(date.getDate());
|
||||
case "d": Std.string(date.getDate());
|
||||
case "mm": doubleDigit(date.getMonth() + 1);
|
||||
case "yyyy": date.getFullYear() + "";
|
||||
case "month": locale.getArray("month")[date.getMonth()];
|
||||
case "Month": firstLetterUp(locale.getArray("month")[date.getMonth()]);
|
||||
case "month_r": locale.getArray("month_r")[date.getMonth()];
|
||||
case "Month_r": firstLetterUp(locale.getArray("month_r")[date.getMonth()]);
|
||||
default: r.matched(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function formatTime(time:Int):String {
|
||||
time = Math.round(time / 1000);
|
||||
var mm:Int = Math.floor(time / 60);
|
||||
var ss:Int = time - mm * 60;
|
||||
return doubleDigit(mm) + ":" + doubleDigit(ss);
|
||||
}
|
||||
|
||||
private static function doubleDigit(num:Int):String {
|
||||
return ((num < 10) ? "0" : "") + num;
|
||||
}
|
||||
|
||||
private static function firstLetterUp(s:String):String {
|
||||
return s.charAt(0).toUpperCase() + s.substring(1);
|
||||
}
|
||||
|
||||
public function formatDateFloat(date:Float, format:String):String {
|
||||
return formatDate(Date.fromTime(date), format);
|
||||
}
|
||||
}
|
||||
7
src/main/haxework/format/IFormatter.hx
Executable file
7
src/main/haxework/format/IFormatter.hx
Executable file
@@ -0,0 +1,7 @@
|
||||
package haxework.format;
|
||||
|
||||
interface IFormatter {
|
||||
public function formatDate(date:Date, format:String):String;
|
||||
public function formatDateFloat(date:Float, format:String):String;
|
||||
public function formatTime(time:Int):String;
|
||||
}
|
||||
57
src/main/haxework/gui/AnimateView.hx
Executable file
57
src/main/haxework/gui/AnimateView.hx
Executable file
@@ -0,0 +1,57 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxe.Timer;
|
||||
import flash.display.Bitmap;
|
||||
import flash.display.BitmapData;
|
||||
import haxework.gui.SpriteView;
|
||||
|
||||
class AnimateView extends SpriteView {
|
||||
|
||||
private var bitmap:Bitmap;
|
||||
public var frames(default, set):Array<BitmapData>;
|
||||
public var interval(default, set):Int;
|
||||
private var frame:Int;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
bitmap = new Bitmap();
|
||||
frames = [];
|
||||
frame = 0;
|
||||
interval = 200;
|
||||
contentAsSprite.addChild(bitmap);
|
||||
changeFrame();
|
||||
}
|
||||
|
||||
private function set_frames(value:Array<BitmapData>):Array<BitmapData> {
|
||||
if (frames != value) {
|
||||
frames = value;
|
||||
frame = 0;
|
||||
changeFrame(true);
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
private function set_interval(value:Int):Int {
|
||||
if (interval != value) {
|
||||
interval = value;
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
|
||||
private function changeFrame(?forse:Bool = false):Void {
|
||||
frame = ++frame % frames.length;
|
||||
bitmap.bitmapData = frames[frame];
|
||||
update();
|
||||
if (!forse) Timer.delay(function() changeFrame(false), interval);
|
||||
}
|
||||
|
||||
override public function update():Void {
|
||||
if (contentSize && bitmap.bitmapData != null) {
|
||||
width = bitmap.bitmapData.width;
|
||||
height = bitmap.bitmapData.height;
|
||||
}
|
||||
super.update();
|
||||
bitmap.x = (width - bitmap.width) / 2;
|
||||
bitmap.y = (height - bitmap.height) / 2;
|
||||
}
|
||||
}
|
||||
116
src/main/haxework/gui/ButtonView.hx
Executable file
116
src/main/haxework/gui/ButtonView.hx
Executable file
@@ -0,0 +1,116 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.events.MouseEvent;
|
||||
import haxework.dispath.Dispatcher;
|
||||
import haxework.dispath.IDispatcher;
|
||||
|
||||
enum ButtonState {
|
||||
UP;
|
||||
OVER;
|
||||
DOWN;
|
||||
}
|
||||
|
||||
class ButtonView extends LabelView {
|
||||
|
||||
public var disabled(default, set):Bool;
|
||||
public var state(get, null):ButtonState;
|
||||
public var dispatcher(default, null):IDispatcher<ButtonViewListener<Dynamic>>;
|
||||
public var onPress(null, set):ButtonViewListener<Dynamic>;
|
||||
|
||||
private var overed:Bool;
|
||||
private var downed:Bool;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
overed = false;
|
||||
downed = false;
|
||||
state = ButtonState.UP;
|
||||
dispatcher = new Dispatcher<ButtonViewListener<Dynamic>>();
|
||||
contentAsSprite.buttonMode = true;
|
||||
contentAsSprite.mouseChildren = false;
|
||||
#if js
|
||||
content.addEventListener(MouseEvent.MOUSE_UP, onMouseClick);
|
||||
#else
|
||||
content.addEventListener(MouseEvent.CLICK, onMouseClick);
|
||||
#end
|
||||
#if !mobile
|
||||
content.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
|
||||
content.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
|
||||
#end
|
||||
content.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
content.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
}
|
||||
|
||||
private function onMouseClick(event:MouseEvent):Void {
|
||||
#if js if (downed) { #end
|
||||
event.stopImmediatePropagation();
|
||||
if (!disabled) dispatcher.dispatch(pressCaller);
|
||||
#if js } #end
|
||||
}
|
||||
|
||||
private function onMouseOver(event:MouseEvent):Void {
|
||||
overed = true;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private function onMouseOut(event:MouseEvent):Void {
|
||||
overed = false;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private function onMouseDown(event:MouseEvent):Void {
|
||||
downed = true;
|
||||
if (content.stage != null) {
|
||||
content.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private function onMouseUp(event:MouseEvent):Void {
|
||||
downed = false;
|
||||
if (content.stage != null) {
|
||||
content.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private function pressCaller(listener:ButtonViewListener<Dynamic>):Void {
|
||||
try {listener.onPress(this);} catch (error:Dynamic) L.e("onPress", "", error);
|
||||
}
|
||||
|
||||
private function set_disabled(value:Bool):Bool {
|
||||
if (disabled != value) {
|
||||
disabled = value;
|
||||
contentAsSprite.buttonMode = !disabled;
|
||||
invalidate();
|
||||
}
|
||||
return disabled;
|
||||
}
|
||||
|
||||
private function get_state():ButtonState {
|
||||
#if mobile
|
||||
return downed ? ButtonState.DOWN : ButtonState.UP;
|
||||
#else
|
||||
return (downed && overed) ? ButtonState.DOWN : overed ? ButtonState.OVER : ButtonState.UP;
|
||||
#end
|
||||
}
|
||||
|
||||
private function set_onPress(value:ButtonViewListener<Dynamic>):ButtonViewListener<Dynamic> {
|
||||
dispatcher.addListener(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public function dispose():Void {
|
||||
dispatcher.removeAllListeners();
|
||||
content.removeEventListener(MouseEvent.CLICK, onMouseClick);
|
||||
content.removeEventListener(MouseEvent.MOUSE_UP, onMouseClick);
|
||||
content.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
|
||||
content.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
|
||||
content.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
content.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
}
|
||||
}
|
||||
|
||||
typedef ButtonViewListener<V:ButtonView> = {
|
||||
public function onPress(view:V):Void;
|
||||
}
|
||||
191
src/main/haxework/gui/GroupView.hx
Executable file
191
src/main/haxework/gui/GroupView.hx
Executable file
@@ -0,0 +1,191 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.display.DisplayObjectContainer;
|
||||
import haxework.gui.core.VAlign;
|
||||
import haxework.gui.core.HAlign;
|
||||
import haxework.gui.layout.DefaultLayout;
|
||||
import haxework.gui.layout.ILayout;
|
||||
import flash.display.Sprite;
|
||||
|
||||
class GroupView extends SpriteView implements IGroupView {
|
||||
public var container(get, null):DisplayObjectContainer;
|
||||
|
||||
public var views(default, set):Array<IView>;
|
||||
public var layout(default, default):ILayout;
|
||||
|
||||
public var layoutVAlign(default, set):VAlign;
|
||||
public var layoutHAlign(default, set):HAlign;
|
||||
public var layoutMargin(default, set):Float = 0;
|
||||
|
||||
public var leftPadding(default, set):Float;
|
||||
public var rightPadding(default, set):Float;
|
||||
public var topPadding(default, set):Float;
|
||||
public var bottomPadding(default, set):Float;
|
||||
public var paddings(null, set):Float;
|
||||
|
||||
private var viewsById:Map<String, IView>;
|
||||
|
||||
public function new(?layout:ILayout) {
|
||||
super();
|
||||
this.layout = layout == null ? new DefaultLayout() : layout;
|
||||
paddings = 0;
|
||||
//layoutMargin = 0;
|
||||
layoutHAlign = HAlign.CENTER;
|
||||
layoutVAlign = VAlign.MIDDLE;
|
||||
views = [];
|
||||
viewsById = new Map<String, IView>();
|
||||
}
|
||||
|
||||
inline private function get_container():DisplayObjectContainer {
|
||||
return contentAsSprite;
|
||||
}
|
||||
|
||||
override public function update():Void {
|
||||
layout.place(this, views);
|
||||
for (view in views) {
|
||||
view.update();
|
||||
if (view.index > -1) {
|
||||
contentAsSprite.setChildIndex(view.content, view.index);
|
||||
}
|
||||
}
|
||||
super.update();
|
||||
}
|
||||
|
||||
public function set_views(value:Array<IView>):Array<IView> {
|
||||
if (views == null) views = [];
|
||||
for (view in value) addView(view);
|
||||
return views;
|
||||
}
|
||||
|
||||
public function addView(view:IView):IView {
|
||||
views.push(view);
|
||||
viewsById.set(view.id, view);
|
||||
if (view.content != null) contentAsSprite.addChild(view.content);
|
||||
view.parent = this;
|
||||
invalidate();
|
||||
return view;
|
||||
}
|
||||
|
||||
public function insertView(view:IView, index:Int):IView {
|
||||
if (index < 0) index = views.length + index;
|
||||
views.insert(index, view);
|
||||
viewsById.set(view.id, view);
|
||||
if (view.content != null) contentAsSprite.addChild(view.content);
|
||||
view.parent = this;
|
||||
invalidate();
|
||||
return view;
|
||||
}
|
||||
|
||||
public function addViewFirst(view:IView):IView {
|
||||
views.unshift(view);
|
||||
viewsById.set(view.id, view);
|
||||
contentAsSprite.addChild(view.content);
|
||||
view.parent = this;
|
||||
invalidate();
|
||||
return view;
|
||||
}
|
||||
|
||||
public function removeView(view:IView):IView {
|
||||
view.parent = null;
|
||||
viewsById.remove(view.id);
|
||||
views.remove(view);
|
||||
if (view.content != null) contentAsSprite.removeChild(view.content);
|
||||
invalidate();
|
||||
return view;
|
||||
}
|
||||
|
||||
public function removeAllViews():Void {
|
||||
while (views.length > 0) {
|
||||
removeView(views[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeViewById(id:String):IView {
|
||||
if (viewsById.exists(id)) {
|
||||
return removeView(viewsById.get(id));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function findViewById<V:IView>(id:String, ?clazz:Class<V>):Null<V> {
|
||||
var idd:Array<String> = id.split(":");
|
||||
if (idd.length > 1) {
|
||||
var id0 = idd.shift();
|
||||
if (viewsById.exists(id0)) {
|
||||
var g:GroupView = findViewById(id0);
|
||||
return g.findViewById(idd.join(":"), clazz);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (viewsById.exists(id)) {
|
||||
return cast viewsById.get(id);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function set_layoutVAlign(value:VAlign):VAlign {
|
||||
if (layoutVAlign != value) {
|
||||
layoutVAlign = value;
|
||||
invalidate();
|
||||
}
|
||||
return layoutVAlign;
|
||||
}
|
||||
|
||||
private function set_layoutHAlign(value:HAlign):HAlign {
|
||||
if (layoutHAlign != value) {
|
||||
layoutHAlign = value;
|
||||
invalidate();
|
||||
}
|
||||
return layoutHAlign;
|
||||
}
|
||||
|
||||
private function set_layoutMargin(value:Float):Float {
|
||||
if (layoutMargin != value) {
|
||||
layoutMargin = value;
|
||||
invalidate();
|
||||
}
|
||||
return layoutMargin;
|
||||
}
|
||||
|
||||
private function set_leftPadding(value:Float):Float {
|
||||
if (leftPadding != value) {
|
||||
leftPadding = value;
|
||||
invalidate();
|
||||
}
|
||||
return leftPadding;
|
||||
}
|
||||
|
||||
private function set_rightPadding(value:Float):Float {
|
||||
if (rightPadding != value) {
|
||||
rightPadding = value;
|
||||
invalidate();
|
||||
}
|
||||
return rightPadding;
|
||||
}
|
||||
|
||||
private function set_topPadding(value:Float):Float {
|
||||
if (topPadding != value) {
|
||||
topPadding = value;
|
||||
invalidate();
|
||||
}
|
||||
return topPadding;
|
||||
}
|
||||
|
||||
private function set_bottomPadding(value:Float):Float {
|
||||
if (bottomPadding != value) {
|
||||
bottomPadding = value;
|
||||
invalidate();
|
||||
}
|
||||
return bottomPadding;
|
||||
}
|
||||
|
||||
private function set_paddings(value:Float):Float {
|
||||
leftPadding = rightPadding = topPadding = bottomPadding = value;
|
||||
invalidate();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
10
src/main/haxework/gui/HGroupView.hx
Executable file
10
src/main/haxework/gui/HGroupView.hx
Executable file
@@ -0,0 +1,10 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxework.gui.layout.HorizontalLayout;
|
||||
|
||||
class HGroupView extends GroupView {
|
||||
|
||||
public function new() {
|
||||
super(new HorizontalLayout());
|
||||
}
|
||||
}
|
||||
9
src/main/haxework/gui/HasPaddings.hx
Normal file
9
src/main/haxework/gui/HasPaddings.hx
Normal file
@@ -0,0 +1,9 @@
|
||||
package haxework.gui;
|
||||
|
||||
interface HasPaddings {
|
||||
public var leftPadding(default, set):Float;
|
||||
public var rightPadding(default, set):Float;
|
||||
public var topPadding(default, set):Float;
|
||||
public var bottomPadding(default, set):Float;
|
||||
public var paddings(null, set):Float;
|
||||
}
|
||||
26
src/main/haxework/gui/IGroupView.hx
Executable file
26
src/main/haxework/gui/IGroupView.hx
Executable file
@@ -0,0 +1,26 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.display.DisplayObjectContainer;
|
||||
import haxework.gui.core.HAlign;
|
||||
import haxework.gui.core.VAlign;
|
||||
import haxework.gui.layout.ILayout;
|
||||
|
||||
interface IGroupView extends IView extends HasPaddings {
|
||||
public var container(get, null):DisplayObjectContainer;
|
||||
|
||||
public var views(default, null):Array<IView>;
|
||||
public var layout(default, default):ILayout;
|
||||
|
||||
public var layoutVAlign(default, set):VAlign;
|
||||
public var layoutHAlign(default, set):HAlign;
|
||||
public var layoutMargin(default, set):Float;
|
||||
|
||||
public function addView(view:IView):IView;
|
||||
public function addViewFirst(view:IView):IView;
|
||||
public function insertView(view:IView, index:Int):IView;
|
||||
public function removeView(view:IView):IView;
|
||||
public function removeAllViews():Void;
|
||||
public function removeViewById(id:String):IView;
|
||||
public function findViewById<V:IView>(id:String, ?clazz:Class<V>):Null<V>;
|
||||
}
|
||||
17
src/main/haxework/gui/ITextView.hx
Executable file
17
src/main/haxework/gui/ITextView.hx
Executable file
@@ -0,0 +1,17 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.text.TextField;
|
||||
import haxework.gui.IView.Content;
|
||||
import flash.text.TextFormatAlign;
|
||||
|
||||
interface ITextView extends IView extends HasPaddings {
|
||||
public var textField(default, null):TextField;
|
||||
public var text(get, set):String;
|
||||
public var align(default, set):TextFormatAlign;
|
||||
//ToDo: font properties to object
|
||||
public var fontFamily(default, set):String;
|
||||
public var fontEmbed(default, set):Bool;
|
||||
public var fontColor(default, set):Int;
|
||||
public var fontSize(default, set):Int;
|
||||
public var fontBold(default, set):Bool;
|
||||
}
|
||||
54
src/main/haxework/gui/IView.hx
Executable file
54
src/main/haxework/gui/IView.hx
Executable file
@@ -0,0 +1,54 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import haxework.gui.core.VAlign;
|
||||
import haxework.gui.core.HAlign;
|
||||
import haxework.gui.skin.ISkin;
|
||||
import haxework.gui.core.SizeType;
|
||||
|
||||
interface IView {
|
||||
public var id(default, default):String;
|
||||
|
||||
public var x(default, set):Float;
|
||||
public var y(default, set):Float;
|
||||
|
||||
public var w(default, set):Float;
|
||||
public var h(default, set):Float;
|
||||
|
||||
public var r(default, set):Float;
|
||||
|
||||
public var widthType(default, null):SizeType;
|
||||
public var heightType(default, null):SizeType;
|
||||
|
||||
public var width(get, set):Float;
|
||||
public var height(get, set):Float;
|
||||
|
||||
public var pWidth(default, set):Float;
|
||||
public var pHeight(default, set):Float;
|
||||
|
||||
public var contentSize(default, set):Bool;
|
||||
|
||||
public var hAlign(default, set):HAlign;
|
||||
public var vAlign(default, set):VAlign;
|
||||
|
||||
public var leftMargin(default, set):Float;
|
||||
public var rightMargin(default, set):Float;
|
||||
public var topMargin(default, set):Float;
|
||||
public var bottomMargin(default, set):Float;
|
||||
public var margins(null, set):Float;
|
||||
|
||||
public var content(default, null):DisplayObject;
|
||||
public var skin(default, set):ISkin<Dynamic>;
|
||||
|
||||
public var parent(default, null):Null<IGroupView>;
|
||||
public var inLayout(default, set):Bool;
|
||||
|
||||
public var visible(default, set):Bool;
|
||||
public var index(default, set):Int;
|
||||
public var mouseEnabled(default, set):Bool;
|
||||
|
||||
public function update():Void;
|
||||
public function invalidate():Void;
|
||||
|
||||
public function remove():Void;
|
||||
}
|
||||
36
src/main/haxework/gui/ImageView.hx
Executable file
36
src/main/haxework/gui/ImageView.hx
Executable file
@@ -0,0 +1,36 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxework.net.ImageLoader;
|
||||
import haxework.gui.utils.DrawUtil.FillType;
|
||||
import haxework.gui.skin.BitmapSkin;
|
||||
import haxework.gui.skin.ButtonBitmapSkin;
|
||||
import flash.display.BitmapData;
|
||||
|
||||
class ImageView extends SpriteView {
|
||||
|
||||
public var image(default, set):BitmapData;
|
||||
public var imageUrl(default, set):String;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
}
|
||||
|
||||
private function set_image(value:BitmapData):BitmapData {
|
||||
if (image != value) {
|
||||
image = value;
|
||||
skin = untyped new BitmapSkin(image, FillType.CONTAIN);
|
||||
invalidate();
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
private function set_imageUrl(value:String):String {
|
||||
if (imageUrl != value) {
|
||||
imageUrl = value;
|
||||
new ImageLoader().GET(imageUrl).success(function(data) {
|
||||
image = data;
|
||||
});
|
||||
}
|
||||
return imageUrl;
|
||||
}
|
||||
}
|
||||
69
src/main/haxework/gui/InputTextField.hx
Normal file
69
src/main/haxework/gui/InputTextField.hx
Normal file
@@ -0,0 +1,69 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.ui.Keyboard;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldType;
|
||||
import flash.events.KeyboardEvent;
|
||||
import haxe.Timer;
|
||||
import flash.events.MouseEvent;
|
||||
|
||||
class InputTextField extends TextField {
|
||||
|
||||
private var focused:Bool;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
#if flash
|
||||
type = TextFieldType.INPUT;
|
||||
#elseif js
|
||||
addEventListener(MouseEvent.CLICK, onMouseClick);
|
||||
#end
|
||||
}
|
||||
|
||||
#if js
|
||||
private function onMouseClick(event:MouseEvent):Void {
|
||||
focused = true;
|
||||
border = true;
|
||||
borderColor = 0x00ff00;
|
||||
Timer.delay(function() {
|
||||
stage.addEventListener(MouseEvent.CLICK, onFocusOut);
|
||||
}, 1);
|
||||
addEventListener(Event.REMOVED_FROM_STAGE, onFocusOut);
|
||||
stage.addEventListener(KeyboardEvent.KEY_UP, onStageKeyUp);
|
||||
stage.addEventListener(KeyboardEvent.KEY_DOWN, onStageKeyDown);
|
||||
}
|
||||
|
||||
private function onStageKeyDown(event:KeyboardEvent):Void {
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
untyped __js__("window.event.preventDefault()");
|
||||
}
|
||||
|
||||
private function onStageKeyUp(event:KeyboardEvent):Void {
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
untyped __js__("window.event.preventDefault()");
|
||||
switch (event.keyCode) {
|
||||
case Keyboard.BACKSPACE:
|
||||
text = event.ctrlKey ? "" : text.substring(0, text.length - 1);
|
||||
case x if (x >= 65 && x <= 90):
|
||||
text += String.fromCharCode(event.keyCode + (event.shiftKey ? 0 : 32));
|
||||
case x if (x >= 48 && x <= 57):
|
||||
text += String.fromCharCode(event.keyCode);
|
||||
case x if (x >= 96 && x <= 105):
|
||||
text += String.fromCharCode(event.keyCode - 48);
|
||||
}
|
||||
}
|
||||
|
||||
private function onFocusOut(_):Void {
|
||||
if (stage != null) {
|
||||
stage.removeEventListener(MouseEvent.CLICK, onFocusOut);
|
||||
stage.removeEventListener(KeyboardEvent.KEY_UP, onStageKeyUp);
|
||||
stage.removeEventListener(KeyboardEvent.KEY_DOWN, onStageKeyDown);
|
||||
}
|
||||
focused = false;
|
||||
border = false;
|
||||
}
|
||||
#end
|
||||
}
|
||||
93
src/main/haxework/gui/InputView.hx
Executable file
93
src/main/haxework/gui/InputView.hx
Executable file
@@ -0,0 +1,93 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxework.dispath.Dispatcher;
|
||||
import haxework.dispath.IDispatcher;
|
||||
import flash.events.Event;
|
||||
import flash.text.TextFormat;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
import flash.text.TextField;
|
||||
import haxework.core.IDisposable;
|
||||
import haxework.core.Const;
|
||||
import flash.events.KeyboardEvent;
|
||||
import flash.events.TextEvent;
|
||||
import flash.text.TextFieldType;
|
||||
|
||||
class InputView extends TextView implements IDisposable {
|
||||
|
||||
public var hint(default, set):String;
|
||||
public var dispatcher(default, null):IDispatcher<InputViewListener>;
|
||||
public var onKeyUp(null, set):InputViewListener;
|
||||
|
||||
private var hintTextField:TextField;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
dispatcher = new Dispatcher<InputViewListener>();
|
||||
textField.addEventListener(Event.CHANGE, onTextChange);
|
||||
textField.addEventListener(KeyboardEvent.KEY_UP, _onKeyUp);
|
||||
textField.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
|
||||
|
||||
hintTextField = buildHintTextField();
|
||||
contentAsSprite.addChild(hintTextField);
|
||||
}
|
||||
|
||||
override private function buildTextField():TextField {
|
||||
return new InputTextField();
|
||||
}
|
||||
|
||||
private function set_hint(value:String):String {
|
||||
if (hint != value) {
|
||||
hint = value;
|
||||
invalidate();
|
||||
}
|
||||
return hint;
|
||||
}
|
||||
|
||||
private function buildHintTextField():TextField {
|
||||
var textField:TextField = new TextField();
|
||||
textField.autoSize = TextFieldAutoSize.NONE;
|
||||
textField.type = TextFieldType.DYNAMIC;
|
||||
textField.multiline = false;
|
||||
textField.defaultTextFormat = new TextFormat("Arial", 16, 0xa0a0a0);
|
||||
textField.mouseEnabled = false;
|
||||
return textField;
|
||||
}
|
||||
|
||||
private function onTextChange(event:Event):Void {
|
||||
hintTextField.visible = (textField.text == "");
|
||||
}
|
||||
|
||||
private function _onKeyUp(event:KeyboardEvent):Void {
|
||||
event.stopImmediatePropagation();
|
||||
dispatcher.dispatch(function(listener) listener.onKeyUp(textField.text));
|
||||
}
|
||||
|
||||
private function onKeyDown(event:KeyboardEvent):Void {
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
override public function update():Void {
|
||||
super.update();
|
||||
var htf:TextFormat = textField.defaultTextFormat;
|
||||
htf.color = 0xa0a0a0;
|
||||
htf.size -= 2;
|
||||
hintTextField.defaultTextFormat = htf;
|
||||
hintTextField.text = hint == null ? "" : hint;
|
||||
placeTextField(hintTextField);
|
||||
}
|
||||
|
||||
public function dispose():Void {
|
||||
textField.removeEventListener(Event.CHANGE, onTextChange);
|
||||
textField.removeEventListener(KeyboardEvent.KEY_UP, _onKeyUp);
|
||||
textField.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
|
||||
}
|
||||
|
||||
private function set_onKeyUp(value:InputViewListener):InputViewListener {
|
||||
dispatcher.addListener(value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
typedef InputViewListener = {
|
||||
public function onKeyUp(text:String):Void;
|
||||
}
|
||||
18
src/main/haxework/gui/LabelView.hx
Executable file
18
src/main/haxework/gui/LabelView.hx
Executable file
@@ -0,0 +1,18 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxework.gui.core.HAlign;
|
||||
import haxework.gui.core.VAlign;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
|
||||
class LabelView extends TextView {
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
fill = false;
|
||||
textField.selectable = false;
|
||||
textField.wordWrap = false;
|
||||
textField.multiline = true;
|
||||
layoutHAlign = HAlign.CENTER;
|
||||
layoutVAlign = VAlign.MIDDLE;
|
||||
}
|
||||
}
|
||||
57
src/main/haxework/gui/MovieView.hx
Executable file
57
src/main/haxework/gui/MovieView.hx
Executable file
@@ -0,0 +1,57 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxework.net.SwfLoader;
|
||||
import flash.display.MovieClip;
|
||||
|
||||
//ToDo: sprite wrapper?
|
||||
class MovieView extends View {
|
||||
|
||||
public var movie(get, set):MovieClip;
|
||||
public var movieUrl(default, set):String;
|
||||
|
||||
public function new(?movie:MovieClip) {
|
||||
super(movie);
|
||||
}
|
||||
|
||||
private function get_movie():MovieClip {
|
||||
return cast content;
|
||||
}
|
||||
|
||||
private function set_movie(value:MovieClip):MovieClip {
|
||||
var index:Int = 0;
|
||||
if (parent != null && content != null) {
|
||||
index = parent.container.getChildIndex(content);
|
||||
parent.container.removeChild(content);
|
||||
}
|
||||
content = value;
|
||||
content.visible = visible;
|
||||
if (parent != null) {
|
||||
parent.container.addChildAt(content, index);
|
||||
}
|
||||
invalidate();
|
||||
return cast content;
|
||||
}
|
||||
|
||||
private function set_movieUrl(value:String):String {
|
||||
movieUrl = value;
|
||||
new SwfLoader().GET(movieUrl)
|
||||
.success(function(data:MovieClip):Void {
|
||||
movie = data;
|
||||
});
|
||||
return movieUrl;
|
||||
}
|
||||
|
||||
override public function update():Void {
|
||||
if (contentSize && content != null) {
|
||||
width = content.loaderInfo.width;
|
||||
height = content.loaderInfo.height;
|
||||
}
|
||||
super.update();
|
||||
if (!contentSize && content != null) {
|
||||
var s:Float = Math.min(width / content.loaderInfo.width, height / content.loaderInfo.height);
|
||||
content.scaleX = content.scaleY = s;
|
||||
content.x = (width - content.loaderInfo.width * s) / 2;
|
||||
content.y = (height - content.loaderInfo.height * s) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/main/haxework/gui/ProgressView.hx
Executable file
29
src/main/haxework/gui/ProgressView.hx
Executable file
@@ -0,0 +1,29 @@
|
||||
package haxework.gui;
|
||||
|
||||
class ProgressView extends SpriteView {
|
||||
|
||||
public var value(default, set):Int;
|
||||
public var max(default, set):Int;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
value = 0;
|
||||
max = 1;
|
||||
}
|
||||
|
||||
private function set_value(value:Int):Int {
|
||||
if (this.value != value) {
|
||||
this.value = value;
|
||||
invalidate();
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
|
||||
private function set_max(value:Int):Int {
|
||||
if (max != value) {
|
||||
max = value;
|
||||
invalidate();
|
||||
}
|
||||
return max;
|
||||
}
|
||||
}
|
||||
55
src/main/haxework/gui/Root.hx
Executable file
55
src/main/haxework/gui/Root.hx
Executable file
@@ -0,0 +1,55 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.errors.Error;
|
||||
import flash.Lib;
|
||||
import flash.display.StageAlign;
|
||||
import flash.display.StageScaleMode;
|
||||
import flash.display.DisplayObject;
|
||||
import flash.events.Event;
|
||||
import flash.display.Sprite;
|
||||
|
||||
class Root {
|
||||
|
||||
public static var instance(default, null):Root;
|
||||
|
||||
public var view(default, null):IView;
|
||||
public var autoSize(default, default):Bool;
|
||||
|
||||
public function new(view:IView, autoSize:Bool = true) {
|
||||
if (instance != null) throw new Error("Only one instance");
|
||||
instance = this;
|
||||
this.view = view;
|
||||
this.autoSize = autoSize;
|
||||
Lib.current.addChild(view.content);
|
||||
var content:DisplayObject = view.content;
|
||||
if (content.stage == null) {
|
||||
content.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
|
||||
} else {
|
||||
onAddedToStage();
|
||||
}
|
||||
View.updater.update();
|
||||
}
|
||||
|
||||
private function onAddedToStage(?_):Void {
|
||||
var content:DisplayObject = view.content;
|
||||
content.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
|
||||
content.stage.scaleMode = StageScaleMode.NO_SCALE;
|
||||
content.stage.align = StageAlign.TOP_LEFT;
|
||||
View.updater.stage = content.stage;
|
||||
|
||||
content.stage.addEventListener(Event.RESIZE, onResize);
|
||||
onResize();
|
||||
}
|
||||
|
||||
private function onResize(?_):Void {
|
||||
var content:DisplayObject = view.content;
|
||||
if (autoSize) {
|
||||
view.width = content.stage.stageWidth;
|
||||
view.height = content.stage.stageHeight;
|
||||
} else {
|
||||
view.x = (content.stage.stageWidth - view.width) / 2;
|
||||
view.y = (content.stage.stageHeight - view.height) / 2;
|
||||
}
|
||||
L.d("Screen", content.stage.stageWidth + "x" + content.stage.stageHeight);
|
||||
}
|
||||
}
|
||||
27
src/main/haxework/gui/SpriteView.hx
Executable file
27
src/main/haxework/gui/SpriteView.hx
Executable file
@@ -0,0 +1,27 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
|
||||
class SpriteView extends View {
|
||||
|
||||
public var contentAsSprite(get, null):Sprite;
|
||||
|
||||
public function new() {
|
||||
super(new Sprite());
|
||||
}
|
||||
|
||||
inline private function get_contentAsSprite():Sprite {
|
||||
return cast content;
|
||||
}
|
||||
|
||||
#if dev_layout
|
||||
override public function update():Void {
|
||||
super.update();
|
||||
var g:Graphics = content.graphics;
|
||||
g.lineStyle(1, 0x00ff00);
|
||||
g.drawRect(0, 0, width, height);
|
||||
g.lineStyle();
|
||||
}
|
||||
#end
|
||||
}
|
||||
285
src/main/haxework/gui/TextView.hx
Executable file
285
src/main/haxework/gui/TextView.hx
Executable file
@@ -0,0 +1,285 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxework.text.TextUtil;
|
||||
import haxework.text.BitmapTextField;
|
||||
import flash.geom.Point;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
import haxework.gui.core.HAlign;
|
||||
import haxework.gui.core.VAlign;
|
||||
import flash.text.TextFormatAlign;
|
||||
import haxework.gui.skin.ISize;
|
||||
import flash.text.TextFormat;
|
||||
import flash.display.Sprite;
|
||||
import flash.text.TextField;
|
||||
|
||||
class TextView extends SpriteView implements ITextView {
|
||||
|
||||
public var textField(default, null):TextField;
|
||||
public var text(get, set):String;
|
||||
private var _text:String;
|
||||
public var align(default, set):TextFormatAlign;
|
||||
public var fontFamily(default, set):String;
|
||||
public var fontEmbed(default, set):Bool;
|
||||
public var fontColor(default, set):Int;
|
||||
public var fontSize(default, set):Int;
|
||||
public var fontBold(default, set):Bool;
|
||||
|
||||
public var layoutHAlign(default, set):HAlign;
|
||||
public var layoutVAlign(default, set):VAlign;
|
||||
public var fill(default, set):Bool = true;
|
||||
|
||||
public var leftPadding(default, set):Float = 0.0;
|
||||
public var rightPadding(default, set):Float = 0.0;
|
||||
public var topPadding(default, set):Float = 0.0;
|
||||
public var bottomPadding(default, set):Float = 0.0;
|
||||
public var paddings(null, set):Float = 0.0;
|
||||
|
||||
public var shadow(default, set):Bool;
|
||||
public var shadowColor(default, set):Int;
|
||||
|
||||
private var textFormat:TextFormat;
|
||||
|
||||
private var _textWidth:Float;
|
||||
private var _textHeight:Float;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
layoutHAlign = HAlign.CENTER;
|
||||
layoutVAlign = VAlign.MIDDLE;
|
||||
textField = buildTextField();
|
||||
textField.width = 1;
|
||||
textField.height = 1;
|
||||
textField.multiline = true;
|
||||
textField.wordWrap = true;
|
||||
#if dev_layout
|
||||
textField.borderColor = 0xff0000;
|
||||
textField.border = true;
|
||||
#end
|
||||
textFormat = textField.defaultTextFormat;
|
||||
textFormat.font = "Arial";
|
||||
textFormat.size = 16;
|
||||
textFormat.leading = 0;
|
||||
textFormat.align = TextFormatAlign.CENTER;
|
||||
contentAsSprite.addChild(textField);
|
||||
}
|
||||
|
||||
private function buildTextField():TextField {
|
||||
return new BitmapTextField();
|
||||
//return new TextField();
|
||||
}
|
||||
|
||||
private function set_fill(value:Bool):Bool {
|
||||
if (fill != value) {
|
||||
fill = value;
|
||||
invalidate();
|
||||
}
|
||||
return fill;
|
||||
}
|
||||
|
||||
private function set_layoutHAlign(value:HAlign):HAlign {
|
||||
if (layoutHAlign != value) {
|
||||
layoutHAlign = value;
|
||||
invalidate();
|
||||
}
|
||||
return layoutHAlign;
|
||||
}
|
||||
|
||||
private function set_layoutVAlign(value:VAlign):VAlign {
|
||||
if (layoutVAlign != value) {
|
||||
layoutVAlign = value;
|
||||
invalidate();
|
||||
}
|
||||
return layoutVAlign;
|
||||
}
|
||||
|
||||
private function get_text():String {
|
||||
return textField.text;
|
||||
}
|
||||
|
||||
private function set_text(value:String):String {
|
||||
if (_text != value) {
|
||||
_text = value;
|
||||
invalidate();
|
||||
}
|
||||
return _text;
|
||||
}
|
||||
|
||||
private function set_align(value:TextFormatAlign):TextFormatAlign {
|
||||
if (align != value) {
|
||||
align = value;
|
||||
textFormat.align = value;
|
||||
invalidate();
|
||||
}
|
||||
return align;
|
||||
}
|
||||
|
||||
private function set_fontFamily(value:String):String {
|
||||
if (fontFamily != value) {
|
||||
fontFamily = value;
|
||||
textFormat.font = fontFamily;
|
||||
invalidate();
|
||||
}
|
||||
return fontFamily;
|
||||
}
|
||||
|
||||
private function set_fontEmbed(value:Bool):Bool {
|
||||
if (fontEmbed != value) {
|
||||
fontEmbed = value;
|
||||
invalidate();
|
||||
}
|
||||
return fontEmbed;
|
||||
}
|
||||
|
||||
private function set_fontColor(value:Int):Int {
|
||||
if (fontColor != value) {
|
||||
fontColor = value;
|
||||
textFormat.color = fontColor;
|
||||
invalidate();
|
||||
}
|
||||
return fontColor;
|
||||
}
|
||||
|
||||
private function set_fontSize(value:Int):Int {
|
||||
if (fontSize != value) {
|
||||
fontSize = value;
|
||||
textFormat.size = fontSize;
|
||||
invalidate();
|
||||
}
|
||||
return fontSize;
|
||||
}
|
||||
|
||||
private function set_fontBold(value:Bool):Bool {
|
||||
if (fontBold != value) {
|
||||
fontBold = value;
|
||||
textFormat.bold = fontBold;
|
||||
invalidate();
|
||||
}
|
||||
return fontBold;
|
||||
}
|
||||
|
||||
private function currentText():String {
|
||||
return _text;
|
||||
}
|
||||
|
||||
private function updateTextSize():Void {
|
||||
var size = TextUtil.getSize(textField);
|
||||
_textWidth = size.x;
|
||||
_textHeight = size.y;
|
||||
}
|
||||
|
||||
override public function update():Void {
|
||||
textField.embedFonts = fontEmbed;
|
||||
textField.defaultTextFormat = textFormat;
|
||||
textField.autoSize = fill ? TextFieldAutoSize.NONE : TextFieldAutoSize.LEFT;
|
||||
var t:String = currentText();
|
||||
if (t != null) textField.text = t;
|
||||
textField.setTextFormat(textFormat);
|
||||
updateTextSize();
|
||||
if (contentSize && !Std.is(skin, ISize)) {
|
||||
#if html5
|
||||
var h = _textHeight;
|
||||
var w = _textWidth;
|
||||
//if (h > textFormat.size * 1.5) h = h / 2;
|
||||
textField.height = h;
|
||||
textField.width = w;
|
||||
#end
|
||||
width = textField.width + leftPadding + rightPadding;
|
||||
height = textField.height + topPadding + bottomPadding;
|
||||
textField.x = leftPadding;
|
||||
textField.y = topPadding;
|
||||
} else {
|
||||
placeTextField(textField);
|
||||
}
|
||||
//ToDo:
|
||||
var t:Point = content.localToGlobal(new Point(textField.x, textField.y));
|
||||
t.x = Math.round(t.x);
|
||||
t.y = Math.round(t.y);
|
||||
t = content.globalToLocal(t);
|
||||
textField.x = t.x;
|
||||
textField.y = t.y;
|
||||
super.update();
|
||||
}
|
||||
|
||||
private function placeTextField(textField:TextField):Void {
|
||||
textField.width = width;
|
||||
textField.height = _textHeight;
|
||||
|
||||
textField.x = switch (layoutHAlign) {
|
||||
case HAlign.NONE: 0;
|
||||
case HAlign.LEFT: leftPadding;
|
||||
case HAlign.CENTER: (width - textField.width) / 2 + leftPadding - rightPadding;
|
||||
case HAlign.RIGHT: width - textField.width - rightPadding;
|
||||
default: 0;
|
||||
}
|
||||
textField.y = switch (layoutVAlign) {
|
||||
case VAlign.NONE: 0;
|
||||
case VAlign.TOP: topPadding;
|
||||
case VAlign.MIDDLE: (height - _textHeight) / 2 + topPadding - bottomPadding;
|
||||
case VAlign.BOTTOM: height - _textHeight - bottomPadding;
|
||||
default: 0;
|
||||
}
|
||||
}
|
||||
|
||||
override private function set_mouseEnabled(value:Bool):Bool {
|
||||
textField.mouseEnabled = value;
|
||||
return super.set_mouseEnabled(value);
|
||||
}
|
||||
|
||||
|
||||
private function set_leftPadding(value:Float):Float {
|
||||
if (leftPadding != value) {
|
||||
leftPadding = value;
|
||||
invalidate();
|
||||
}
|
||||
return leftPadding;
|
||||
}
|
||||
|
||||
private function set_rightPadding(value:Float):Float {
|
||||
if (rightPadding != value) {
|
||||
rightPadding = value;
|
||||
invalidate();
|
||||
}
|
||||
return rightPadding;
|
||||
}
|
||||
|
||||
private function set_topPadding(value:Float):Float {
|
||||
if (topPadding != value) {
|
||||
topPadding = value;
|
||||
invalidate();
|
||||
}
|
||||
return topPadding;
|
||||
}
|
||||
|
||||
private function set_bottomPadding(value:Float):Float {
|
||||
if (bottomPadding != value) {
|
||||
bottomPadding = value;
|
||||
invalidate();
|
||||
}
|
||||
return bottomPadding;
|
||||
}
|
||||
|
||||
private function set_paddings(value:Float):Float {
|
||||
leftPadding = rightPadding = topPadding = bottomPadding = value;
|
||||
invalidate();
|
||||
return value;
|
||||
}
|
||||
|
||||
private function set_shadow(value) {
|
||||
if (Std.is(textField, BitmapTextField)) {
|
||||
cast(textField, BitmapTextField).shadow = value;
|
||||
return cast(textField, BitmapTextField).shadow;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private function set_shadowColor(value) {
|
||||
if (Std.is(textField, BitmapTextField)) {
|
||||
cast(textField, BitmapTextField).shadowColor = value;
|
||||
cast(textField, BitmapTextField).shadow = true;
|
||||
return cast(textField, BitmapTextField).shadowColor;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/main/haxework/gui/ToggleButtonView.hx
Executable file
44
src/main/haxework/gui/ToggleButtonView.hx
Executable file
@@ -0,0 +1,44 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.display.Sprite;
|
||||
import haxework.gui.skin.ISkin;
|
||||
|
||||
class ToggleButtonView extends ButtonView {
|
||||
|
||||
public var on(default, set):Bool;
|
||||
public var onSkin(default, set):ISkin<Dynamic>;
|
||||
|
||||
public var onText(default, set):String;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
}
|
||||
|
||||
private function set_on(value:Bool):Bool {
|
||||
on = value;
|
||||
invalidate();
|
||||
return on;
|
||||
}
|
||||
|
||||
private function set_onSkin(value:ISkin<Dynamic>):ISkin<Dynamic> {
|
||||
onSkin = value;
|
||||
invalidate();
|
||||
return onSkin;
|
||||
}
|
||||
|
||||
override private function currentSkin():ISkin<Dynamic> {
|
||||
return on ? onSkin : skin;
|
||||
}
|
||||
|
||||
private function set_onText(value:String):String {
|
||||
if (onText != value) {
|
||||
onText = value;
|
||||
invalidate;
|
||||
}
|
||||
return onText;
|
||||
}
|
||||
|
||||
override private function currentText():String {
|
||||
return on && onText != null ? onText : super.currentText();
|
||||
}
|
||||
}
|
||||
10
src/main/haxework/gui/VGroupView.hx
Executable file
10
src/main/haxework/gui/VGroupView.hx
Executable file
@@ -0,0 +1,10 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxework.gui.layout.VerticalLayout;
|
||||
|
||||
class VGroupView extends GroupView {
|
||||
|
||||
public function new() {
|
||||
super(new VerticalLayout());
|
||||
}
|
||||
}
|
||||
342
src/main/haxework/gui/View.hx
Executable file
342
src/main/haxework/gui/View.hx
Executable file
@@ -0,0 +1,342 @@
|
||||
package haxework.gui;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.display.DisplayObject;
|
||||
import flash.errors.Error;
|
||||
import haxework.gui.skin.ISize;
|
||||
import haxework.gui.core.SizeType;
|
||||
import haxework.gui.core.HAlign;
|
||||
import haxework.gui.core.VAlign;
|
||||
import flash.events.Event;
|
||||
import flash.display.Stage;
|
||||
import haxework.gui.skin.FakeSkin;
|
||||
import haxework.gui.skin.ISkin;
|
||||
|
||||
import flash.display.Sprite;
|
||||
|
||||
class View implements IView {
|
||||
|
||||
private static var counter:Int = 0;
|
||||
public static var updater(default, null):Updater = new Updater();
|
||||
|
||||
public var id(default, default):String;
|
||||
|
||||
public var x(default, set):Float;
|
||||
public var y(default, set):Float;
|
||||
|
||||
public var w(default, set):Float;
|
||||
public var h(default, set):Float;
|
||||
|
||||
public var r(default, set):Float;
|
||||
|
||||
public var widthType(default, null):SizeType;
|
||||
public var heightType(default, null):SizeType;
|
||||
|
||||
public var width(get, set):Float;
|
||||
public var height(get, set):Float;
|
||||
|
||||
public var pWidth(default, set):Float;
|
||||
public var pHeight(default, set):Float;
|
||||
|
||||
public var contentSize(default, set):Bool;
|
||||
|
||||
public var hAlign(default, set):HAlign;
|
||||
public var vAlign(default, set):VAlign;
|
||||
|
||||
public var leftMargin(default, set):Float;
|
||||
public var rightMargin(default, set):Float;
|
||||
public var topMargin(default, set):Float;
|
||||
public var bottomMargin(default, set):Float;
|
||||
public var margins(null, set):Float;
|
||||
|
||||
public var content(default, null):DisplayObject;
|
||||
public var skin(default, set):ISkin<Dynamic>;
|
||||
|
||||
public var parent(default, null):Null<IGroupView>;
|
||||
public var inLayout(default, set):Bool;
|
||||
|
||||
public var visible(default, set):Bool;
|
||||
public var index(default, set):Int;
|
||||
public var mouseEnabled(default, set):Bool = true;
|
||||
|
||||
public function new(content:DisplayObject) {
|
||||
id = Type.getClassName(Type.getClass(this)) + counter++;
|
||||
this.content = content;
|
||||
x = 0;
|
||||
y = 0;
|
||||
width = 1;
|
||||
height = 1;
|
||||
margins = 0;
|
||||
vAlign = VAlign.NONE;
|
||||
hAlign = HAlign.NONE;
|
||||
inLayout = true;
|
||||
visible = true;
|
||||
index = -1;
|
||||
}
|
||||
|
||||
private function currentSkin():ISkin<Dynamic> {
|
||||
return skin;
|
||||
}
|
||||
|
||||
public function invalidate():Void {
|
||||
updater.invalidate(this);
|
||||
}
|
||||
|
||||
private function invalidateParent():Void {
|
||||
if (parent != null)
|
||||
updater.invalidate(parent);
|
||||
}
|
||||
|
||||
public function update():Void {
|
||||
if (content != null) {
|
||||
content.x = x;
|
||||
content.y = y;
|
||||
}
|
||||
var skin:ISkin<Dynamic> = currentSkin();
|
||||
if (contentSize && skin != null && Std.is(skin, ISize)) {
|
||||
var size:ISize = cast(skin, ISize);
|
||||
if (!Math.isNaN(size.width)) width = size.width;
|
||||
if (!Math.isNaN(size.height)) height = size.height;
|
||||
}
|
||||
if (skin != null) skin.draw(this);
|
||||
}
|
||||
|
||||
public function remove():Void {
|
||||
if (parent != null) parent.removeView(this);
|
||||
}
|
||||
|
||||
private function set_x(value:Float):Float {
|
||||
if (x != value) {
|
||||
x = value;
|
||||
invalidate();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
private function set_y(value:Float):Float {
|
||||
if (y != value) {
|
||||
y = value;
|
||||
invalidate();
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
private function set_w(value:Float):Float {
|
||||
if (w != value) {
|
||||
w = value;
|
||||
if (!Math.isNaN(r) && r > 0) h = w / r;
|
||||
invalidate();
|
||||
}
|
||||
return w;
|
||||
}
|
||||
private function set_h(value:Float):Float {
|
||||
if (h != value) {
|
||||
h = value;
|
||||
if (!Math.isNaN(r) && r > 0) w = h * r;
|
||||
invalidate();
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
private function set_r(value:Float):Float {
|
||||
if (r != value) {
|
||||
r = value;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private function get_width():Float {
|
||||
return w;
|
||||
}
|
||||
|
||||
private function get_height():Float {
|
||||
return h;
|
||||
}
|
||||
|
||||
private function set_width(value:Float):Float {
|
||||
if (w != value || widthType != SizeType.NORMAL) {
|
||||
w = value;
|
||||
widthType = SizeType.NORMAL;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
private function set_height(value:Float):Float {
|
||||
if (h != value || heightType != SizeType.NORMAL) {
|
||||
h = value;
|
||||
heightType = SizeType.NORMAL;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
private function set_pWidth(value:Float):Float {
|
||||
if (pWidth != value || widthType != SizeType.PERCENT) {
|
||||
pWidth = value;
|
||||
widthType = SizeType.PERCENT;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return pWidth;
|
||||
}
|
||||
|
||||
private function set_pHeight(value:Float):Float {
|
||||
if (pHeight != value || heightType != SizeType.PERCENT) {
|
||||
pHeight = value;
|
||||
heightType = SizeType.PERCENT;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return pHeight;
|
||||
}
|
||||
|
||||
private function set_contentSize(value:Bool):Bool {
|
||||
if (contentSize != value) {
|
||||
contentSize = value;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return contentSize;
|
||||
}
|
||||
|
||||
private function set_hAlign(value:HAlign):HAlign {
|
||||
if (hAlign != value) {
|
||||
hAlign = value;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return hAlign;
|
||||
}
|
||||
|
||||
private function set_vAlign(value:VAlign):VAlign {
|
||||
if (vAlign != value) {
|
||||
vAlign = value;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return vAlign;
|
||||
}
|
||||
|
||||
private function set_leftMargin(value:Float):Float {
|
||||
if (leftMargin != value) {
|
||||
leftMargin = value;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return leftMargin;
|
||||
}
|
||||
|
||||
private function set_rightMargin(value:Float):Float {
|
||||
if (rightMargin != value) {
|
||||
rightMargin = value;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return rightMargin;
|
||||
}
|
||||
|
||||
private function set_topMargin(value:Float):Float {
|
||||
if (topMargin != value) {
|
||||
topMargin = value;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return topMargin;
|
||||
}
|
||||
|
||||
private function set_bottomMargin(value:Float):Float {
|
||||
if (bottomMargin != value) {
|
||||
bottomMargin = value;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
}
|
||||
return bottomMargin;
|
||||
}
|
||||
|
||||
private function set_margins(value:Float):Float {
|
||||
leftMargin = rightMargin = topMargin = bottomMargin = value;
|
||||
invalidate();
|
||||
invalidateParent();
|
||||
return value;
|
||||
}
|
||||
|
||||
private function set_skin(value:ISkin<Dynamic>):ISkin<Dynamic> {
|
||||
skin = value;
|
||||
invalidate();
|
||||
return skin;
|
||||
}
|
||||
|
||||
private function set_inLayout(value:Bool):Bool {
|
||||
if (inLayout != value) {
|
||||
inLayout = value;
|
||||
invalidateParent();
|
||||
}
|
||||
return inLayout;
|
||||
}
|
||||
|
||||
private function set_visible(value:Bool):Bool {
|
||||
if (visible != value) {
|
||||
visible = value;
|
||||
if (content != null) content.visible = visible;
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
private function set_index(value:Int):Int {
|
||||
if (index != value) {
|
||||
index = value;
|
||||
invalidateParent();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private function set_mouseEnabled(value:Bool):Bool {
|
||||
if (mouseEnabled != value) {
|
||||
mouseEnabled = value;
|
||||
if (content != null && Std.is(content, InteractiveObject)) {
|
||||
cast(content, InteractiveObject).mouseEnabled = mouseEnabled;
|
||||
}
|
||||
}
|
||||
return mouseEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Updater {
|
||||
|
||||
public var stage(null, set):Stage;
|
||||
private var invalidated:Array<Dynamic>;
|
||||
|
||||
public function new() {
|
||||
invalidated = [];
|
||||
}
|
||||
|
||||
private function set_stage(value:Stage):Stage {
|
||||
value.addEventListener(Event.ENTER_FRAME, update);
|
||||
return value;
|
||||
}
|
||||
|
||||
public function invalidate(view:IView):Void {
|
||||
if (Lambda.indexOf(invalidated, view) == -1) invalidated.push(view);
|
||||
}
|
||||
|
||||
public function update(?_):Void {
|
||||
var t = Date.now().getTime();
|
||||
while (invalidated.length > 0) {
|
||||
var v = null;
|
||||
try {
|
||||
v = invalidated.shift();
|
||||
v.update();
|
||||
} catch (error:Dynamic) {
|
||||
L.e("Update", v + "", error);
|
||||
}
|
||||
}
|
||||
t = Date.now().getTime() - t;
|
||||
if (t > 10) trace("UPDATE(" + t + ")");
|
||||
}
|
||||
|
||||
}
|
||||
14
src/main/haxework/gui/ViewBuilder.hx
Executable file
14
src/main/haxework/gui/ViewBuilder.hx
Executable file
@@ -0,0 +1,14 @@
|
||||
package haxework.gui;
|
||||
|
||||
import haxework.gui.build.Builder;
|
||||
|
||||
@:remove @:autoBuild(haxework.gui.ViewBuilderImpl.build())
|
||||
extern interface ViewBuilder {}
|
||||
|
||||
class ViewBuilderImpl {
|
||||
#if macro
|
||||
public static function build() {
|
||||
return new Builder().build();
|
||||
}
|
||||
#end
|
||||
}
|
||||
188
src/main/haxework/gui/build/Builder.hx
Executable file
188
src/main/haxework/gui/build/Builder.hx
Executable file
@@ -0,0 +1,188 @@
|
||||
package haxework.gui.build;
|
||||
#if macro
|
||||
|
||||
import haxe.macro.Context;
|
||||
import haxework.gui.build.PositionJsonParser;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Expr.Field;
|
||||
|
||||
class Builder {
|
||||
|
||||
private var templateFile:String;
|
||||
private var templateKey:String;
|
||||
private var styleFile:String;
|
||||
|
||||
private var template:Dynamic;
|
||||
private var fields:Array<Field>;
|
||||
private var exprs:Array<Expr>;
|
||||
private var style:Dynamic;
|
||||
|
||||
private var i:Int;
|
||||
|
||||
public function new() {
|
||||
var templateMeta = BuilderUtil.getMeta(":template");
|
||||
var templatePath = templateMeta[0].split("@");
|
||||
templateFile = Context.resolvePath(templatePath[0]);
|
||||
templateKey = templatePath[1];
|
||||
|
||||
template = BuilderUtil.loadFile(templateFile);
|
||||
if (templateKey != null) template = Reflect.field(template, templateKey);
|
||||
|
||||
if (templateMeta[1] != null) {
|
||||
styleFile = Context.resolvePath(templateMeta[1]);
|
||||
style = BuilderUtil.loadFile(styleFile);
|
||||
}
|
||||
|
||||
fields = Context.getBuildFields();
|
||||
exprs = [];
|
||||
i = 0;
|
||||
}
|
||||
|
||||
private function getPosition(?position:JsonKeyPosition):Position {
|
||||
var min = position == null ? 1 : position.min + 32; // :-(
|
||||
var max = position == null ? 1 : position.max + 32;
|
||||
var file = position == null || position.file == null ? templateFile : position.file;
|
||||
return Context.makePosition({min:min, max:max, file:file});
|
||||
}
|
||||
|
||||
private function specialValue(name:String, key:String, a:Array<String>, position:JsonKeyPosition):Dynamic {
|
||||
return switch (a[0]) {
|
||||
case "asset":
|
||||
switch (a[1]) {
|
||||
case "image":
|
||||
"openfl.Assets.getBitmapData(\"" + a[2] + "\")";
|
||||
case _:
|
||||
a[2];
|
||||
}
|
||||
case "res":
|
||||
var res = "haxework.provider.Provider.get(haxework.resources.IResources)." + a[1];
|
||||
var bindExpr = res + ".bind(\"" + a[2] + "\", " + name + ", \"" + key + "\")";
|
||||
exprs.push(Context.parse(bindExpr, getPosition(position)));
|
||||
//res + ".get(\"" + a[2] + "\")";
|
||||
null;
|
||||
case "locale":
|
||||
"new haxework.locale.LString(\"" + a[1] + "\")";
|
||||
case "class":
|
||||
a[1];
|
||||
case "layout":
|
||||
var template = BuilderUtil.loadFile(a[1]);
|
||||
return getValue(name, key, template, position);
|
||||
case "link":
|
||||
"(links == null) ? untyped this : Reflect.field(links, \"" + a[1] + "\")";
|
||||
case _:
|
||||
throw "Unsupported prefix \"" + a[0] + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
private function getValue(name:String, key:String, value:Dynamic, position:JsonKeyPosition):Dynamic {
|
||||
return if (Std.is(value, Array)) {
|
||||
value.map(function(v) {
|
||||
return getValue(null, null, v, position);
|
||||
});
|
||||
} else if (Std.is(value, String)) {
|
||||
if (value.charAt(0) == "@") {
|
||||
specialValue(name, key, value.substring(1, value.length).split(":"), position);
|
||||
} else if (~/(0x|#)[A-Fa-f\d]{6}/.match(value)) {
|
||||
Std.parseInt(StringTools.replace(Std.string(value), "#", "0x"));
|
||||
} else {
|
||||
"\"" + value + "\"";
|
||||
}
|
||||
} else if (Std.is(value, Float) || (Std.is(value, Bool))) {
|
||||
value;
|
||||
} else if (value != null) {
|
||||
if (Reflect.hasField(value, "type")) {
|
||||
var n = "a" + i++;
|
||||
var type = Reflect.field(value, "type");
|
||||
exprs.push(Context.parse("var " + n + " = new " + type + "()", getPosition(position)));
|
||||
createElement(value, n);
|
||||
n;
|
||||
} else {
|
||||
Context.error("Need type field", getPosition(position));
|
||||
null;
|
||||
}
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
}
|
||||
|
||||
private function createElement(template:Dynamic, name:String):String {
|
||||
if (Reflect.hasField(template, "style")) {
|
||||
var s = Reflect.field(style, Reflect.field(template, "style"));
|
||||
for (key in Reflect.fields(s)) {
|
||||
if (key.charAt(0) != "$" && !Reflect.hasField(template, key)) {
|
||||
Reflect.setField(template, key, Reflect.field(s, key));
|
||||
Reflect.setField(template, "$" + key, Reflect.field(s, "$" + key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Reflect.hasField(template, "id")) {
|
||||
var id = Reflect.field(template, "id");
|
||||
var type = Reflect.field(template, "type");
|
||||
var expr = Context.parse("var a:" + type, getPosition());
|
||||
var type = switch (expr.expr) {
|
||||
case EVars(vars): vars[0].type;
|
||||
case _: null;
|
||||
}
|
||||
fields.push({
|
||||
name: id,
|
||||
access: [APublic],
|
||||
pos: getPosition(),
|
||||
kind: FProp("default", "null", type)
|
||||
});
|
||||
exprs.push(Context.parse("this." + id + " = " + name, getPosition()));
|
||||
}
|
||||
|
||||
for (key in Reflect.fields(template)) {
|
||||
if (key.charAt(0) == "$" || ["type", "style"].indexOf(key) > -1) continue;
|
||||
var position = Reflect.field(template, "$" + key);
|
||||
var value = getValue(name, key, Reflect.field(template, key), position);
|
||||
if (value != null) {
|
||||
exprs.push(Context.parse(name + "." + key + " = " + value, getPosition(position)));
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
public function build():Array<Field> {
|
||||
createElement(template, "this");
|
||||
|
||||
var init = false;
|
||||
for (f in fields) if (f.name == "init") {
|
||||
init = true;
|
||||
break;
|
||||
}
|
||||
|
||||
fields.push({
|
||||
name: "build",
|
||||
access: [APublic],
|
||||
pos: getPosition(),
|
||||
kind: FFun({
|
||||
args: [{name:"links", type:TPath({name:"Dynamic", pack:[], params:[]}), opt:true, value:null}],
|
||||
expr: macro $b{exprs},
|
||||
params: [],
|
||||
ret: null
|
||||
})
|
||||
});
|
||||
|
||||
var contstrExprs = [];
|
||||
contstrExprs.push(macro super());
|
||||
contstrExprs.push(macro build(links));
|
||||
if (init) contstrExprs.push(macro init());
|
||||
|
||||
fields.push({
|
||||
name: "new",
|
||||
access: [APublic],
|
||||
pos: getPosition(),
|
||||
kind: FFun({
|
||||
args: [{name:"links", type:TPath({name:"Dynamic", pack:[], params:[]}), opt:true, value:null}],
|
||||
expr: macro $b{contstrExprs},
|
||||
params: [],
|
||||
ret: null
|
||||
})
|
||||
});
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
#end
|
||||
30
src/main/haxework/gui/build/BuilderUtil.hx
Executable file
30
src/main/haxework/gui/build/BuilderUtil.hx
Executable file
@@ -0,0 +1,30 @@
|
||||
package haxework.gui.build;
|
||||
#if macro
|
||||
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr.Constant;
|
||||
import haxe.macro.Expr.ExprDef;
|
||||
|
||||
class BuilderUtil {
|
||||
|
||||
public static function loadFile(path:String, json:Bool = true) {
|
||||
Context.registerModuleDependency(Context.getLocalModule(), path);
|
||||
var content = sys.io.File.getContent(path);
|
||||
Context.parse(content, Context.makePosition({min:0, max:0, file:path}));
|
||||
return json ? PositionJsonParser.parse(content, path) : content;
|
||||
}
|
||||
|
||||
public static function getMeta(key:String):Array<String> {
|
||||
var c = Context.getLocalClass().get();
|
||||
for (meta in c.meta.get()) {
|
||||
if (meta.name == key) {
|
||||
return meta.params.map(function(param) return switch(param.expr) {
|
||||
case ExprDef.EConst(Constant.CString(value)): value;
|
||||
case _: null;
|
||||
});
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
#end
|
||||
237
src/main/haxework/gui/build/PositionJsonParser.hx
Executable file
237
src/main/haxework/gui/build/PositionJsonParser.hx
Executable file
@@ -0,0 +1,237 @@
|
||||
package haxework.gui.build;
|
||||
|
||||
typedef JsonKeyPosition = {
|
||||
var min:Int;
|
||||
var max:Int;
|
||||
var file:String;
|
||||
}
|
||||
|
||||
class PositionJsonParser {
|
||||
|
||||
static public inline function parse(str : String, file : String) : Dynamic {
|
||||
return new PositionJsonParser(str, file).parseRec();
|
||||
}
|
||||
|
||||
var str : String;
|
||||
var pos : Int;
|
||||
var file : String;
|
||||
|
||||
function new( str : String, file:String ) {
|
||||
this.str = str;
|
||||
this.pos = 0;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
function getKeyPosition():JsonKeyPosition {
|
||||
return {
|
||||
min: pos,
|
||||
max: pos,
|
||||
file: file
|
||||
}
|
||||
}
|
||||
|
||||
function parseRec() : Dynamic {
|
||||
while( true ) {
|
||||
var c = nextChar();
|
||||
switch( c ) {
|
||||
case ' '.code, '\r'.code, '\n'.code, '\t'.code:
|
||||
// loop
|
||||
case '{'.code:
|
||||
var obj = {}, field = null, comma : Null<Bool> = null;
|
||||
var position = null;
|
||||
while( true ) {
|
||||
var c = nextChar();
|
||||
switch( c ) {
|
||||
case ' '.code, '\r'.code, '\n'.code, '\t'.code:
|
||||
// loop
|
||||
case '}'.code:
|
||||
if( field != null || comma == false )
|
||||
invalidChar();
|
||||
return obj;
|
||||
case ':'.code:
|
||||
if( field == null )
|
||||
invalidChar();
|
||||
Reflect.setField(obj,field,parseRec());
|
||||
Reflect.setField(obj,"$" + field,position);
|
||||
field = null;
|
||||
comma = true;
|
||||
case ','.code:
|
||||
if( comma ) comma = false else invalidChar();
|
||||
case '"'.code:
|
||||
if( comma ) invalidChar();
|
||||
position = getKeyPosition();
|
||||
field = parseString();
|
||||
default:
|
||||
invalidChar();
|
||||
}
|
||||
}
|
||||
case '['.code:
|
||||
var arr = [], comma : Null<Bool> = null;
|
||||
while( true ) {
|
||||
var c = nextChar();
|
||||
switch( c ) {
|
||||
case ' '.code, '\r'.code, '\n'.code, '\t'.code:
|
||||
// loop
|
||||
case ']'.code:
|
||||
if( comma == false ) invalidChar();
|
||||
return arr;
|
||||
case ','.code:
|
||||
if( comma ) comma = false else invalidChar();
|
||||
default:
|
||||
if( comma ) invalidChar();
|
||||
pos--;
|
||||
arr.push(parseRec());
|
||||
comma = true;
|
||||
}
|
||||
}
|
||||
case 't'.code:
|
||||
var save = pos;
|
||||
if( nextChar() != 'r'.code || nextChar() != 'u'.code || nextChar() != 'e'.code ) {
|
||||
pos = save;
|
||||
invalidChar();
|
||||
}
|
||||
return true;
|
||||
case 'f'.code:
|
||||
var save = pos;
|
||||
if( nextChar() != 'a'.code || nextChar() != 'l'.code || nextChar() != 's'.code || nextChar() != 'e'.code ) {
|
||||
pos = save;
|
||||
invalidChar();
|
||||
}
|
||||
return false;
|
||||
case 'n'.code:
|
||||
var save = pos;
|
||||
if( nextChar() != 'u'.code || nextChar() != 'l'.code || nextChar() != 'l'.code ) {
|
||||
pos = save;
|
||||
invalidChar();
|
||||
}
|
||||
return null;
|
||||
case '"'.code:
|
||||
return parseString();
|
||||
case '0'.code, '1'.code,'2'.code,'3'.code,'4'.code,'5'.code,'6'.code,'7'.code,'8'.code,'9'.code,'-'.code:
|
||||
return parseNumber(c);
|
||||
default:
|
||||
invalidChar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseString() {
|
||||
var start = pos;
|
||||
var buf = null;
|
||||
while( true ) {
|
||||
var c = nextChar();
|
||||
if( c == '"'.code )
|
||||
break;
|
||||
if( c == '\\'.code ) {
|
||||
if (buf == null) {
|
||||
buf = new StringBuf();
|
||||
}
|
||||
buf.addSub(str,start, pos - start - 1);
|
||||
c = nextChar();
|
||||
switch( c ) {
|
||||
case "r".code: buf.addChar("\r".code);
|
||||
case "n".code: buf.addChar("\n".code);
|
||||
case "t".code: buf.addChar("\t".code);
|
||||
case "b".code: buf.addChar(8);
|
||||
case "f".code: buf.addChar(12);
|
||||
case "/".code, '\\'.code, '"'.code: buf.addChar(c);
|
||||
case 'u'.code:
|
||||
var uc = Std.parseInt("0x" + str.substr(pos, 4));
|
||||
pos += 4;
|
||||
#if (neko || php || cpp)
|
||||
if( uc <= 0x7F )
|
||||
buf.addChar(uc);
|
||||
else if( uc <= 0x7FF ) {
|
||||
buf.addChar(0xC0 | (uc >> 6));
|
||||
buf.addChar(0x80 | (uc & 63));
|
||||
} else if( uc <= 0xFFFF ) {
|
||||
buf.addChar(0xE0 | (uc >> 12));
|
||||
buf.addChar(0x80 | ((uc >> 6) & 63));
|
||||
buf.addChar(0x80 | (uc & 63));
|
||||
} else {
|
||||
buf.addChar(0xF0 | (uc >> 18));
|
||||
buf.addChar(0x80 | ((uc >> 12) & 63));
|
||||
buf.addChar(0x80 | ((uc >> 6) & 63));
|
||||
buf.addChar(0x80 | (uc & 63));
|
||||
}
|
||||
#else
|
||||
buf.addChar(uc);
|
||||
#end
|
||||
default:
|
||||
throw "Invalid escape sequence \\" + String.fromCharCode(c) + " at position " + (pos - 1);
|
||||
}
|
||||
start = pos;
|
||||
}
|
||||
#if (neko || php || cpp)
|
||||
// ensure utf8 chars are not cut
|
||||
else if( c >= 0x80 ) {
|
||||
pos++;
|
||||
if( c >= 0xFC ) pos += 4;
|
||||
else if( c >= 0xF8 ) pos += 3;
|
||||
else if( c >= 0xF0 ) pos += 2;
|
||||
else if( c >= 0xE0 ) pos++;
|
||||
}
|
||||
#end
|
||||
else if( StringTools.isEof(c) )
|
||||
throw "Unclosed string";
|
||||
}
|
||||
if (buf == null) {
|
||||
return str.substr(start, pos - start - 1);
|
||||
}
|
||||
else {
|
||||
buf.addSub(str,start, pos - start - 1);
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
inline function parseNumber( c : Int ) : Dynamic {
|
||||
var start = pos - 1;
|
||||
var minus = c == '-'.code, digit = !minus, zero = c == '0'.code;
|
||||
var point = false, e = false, pm = false, end = false;
|
||||
while( true ) {
|
||||
c = nextChar();
|
||||
switch( c ) {
|
||||
case '0'.code :
|
||||
if (zero && !point) invalidNumber(start);
|
||||
if (minus) {
|
||||
minus = false; zero = true;
|
||||
}
|
||||
digit = true;
|
||||
case '1'.code,'2'.code,'3'.code,'4'.code,'5'.code,'6'.code,'7'.code,'8'.code,'9'.code :
|
||||
if (zero && !point) invalidNumber(start);
|
||||
if (minus) minus = false;
|
||||
digit = true; zero = false;
|
||||
case '.'.code :
|
||||
if (minus || point) invalidNumber(start);
|
||||
digit = false; point = true;
|
||||
case 'e'.code, 'E'.code :
|
||||
if (minus || zero || e) invalidNumber(start);
|
||||
digit = false; e = true;
|
||||
case '+'.code, '-'.code :
|
||||
if (!e || pm) invalidNumber(start);
|
||||
digit = false; pm = true;
|
||||
default :
|
||||
if (!digit) invalidNumber(start);
|
||||
pos--;
|
||||
end = true;
|
||||
}
|
||||
if (end) break;
|
||||
}
|
||||
var f = Std.parseFloat(str.substr(start, pos - start));
|
||||
var i = Std.int(f);
|
||||
return if( i == f ) i else f;
|
||||
}
|
||||
|
||||
inline function nextChar() {
|
||||
return StringTools.fastCodeAt(str,pos++);
|
||||
}
|
||||
|
||||
function invalidChar() {
|
||||
pos--; // rewind
|
||||
throw "Invalid char "+StringTools.fastCodeAt(str,pos)+" at position "+pos;
|
||||
}
|
||||
|
||||
function invalidNumber( start : Int ) {
|
||||
throw "Invalid number at position "+start+": " + str.substr(start, pos - start);
|
||||
}
|
||||
}
|
||||
6
src/main/haxework/gui/core/Direction.hx
Executable file
6
src/main/haxework/gui/core/Direction.hx
Executable file
@@ -0,0 +1,6 @@
|
||||
package haxework.gui.core;
|
||||
|
||||
enum Direction {
|
||||
HORIZONTAL;
|
||||
VERTICAL;
|
||||
}
|
||||
8
src/main/haxework/gui/core/HAlign.hx
Executable file
8
src/main/haxework/gui/core/HAlign.hx
Executable file
@@ -0,0 +1,8 @@
|
||||
package haxework.gui.core;
|
||||
|
||||
@:enum abstract HAlign(String) from String to String {
|
||||
var NONE = "NONE";
|
||||
var LEFT = "LEFT";
|
||||
var CENTER = "CENTER";
|
||||
var RIGHT = "RIGHT";
|
||||
}
|
||||
6
src/main/haxework/gui/core/SizeType.hx
Executable file
6
src/main/haxework/gui/core/SizeType.hx
Executable file
@@ -0,0 +1,6 @@
|
||||
package haxework.gui.core;
|
||||
|
||||
enum SizeType {
|
||||
NORMAL;
|
||||
PERCENT;
|
||||
}
|
||||
8
src/main/haxework/gui/core/VAlign.hx
Executable file
8
src/main/haxework/gui/core/VAlign.hx
Executable file
@@ -0,0 +1,8 @@
|
||||
package haxework.gui.core;
|
||||
|
||||
@:enum abstract VAlign(String) from String to String {
|
||||
var NONE = "NONE";
|
||||
var TOP = "TOP";
|
||||
var MIDDLE = "MIDDLE";
|
||||
var BOTTOM = "BOTTOM";
|
||||
}
|
||||
72
src/main/haxework/gui/frame/FrameSwitcher.hx
Executable file
72
src/main/haxework/gui/frame/FrameSwitcher.hx
Executable file
@@ -0,0 +1,72 @@
|
||||
package haxework.gui.frame;
|
||||
|
||||
import haxework.animate.IAnimate;
|
||||
import flash.display.Sprite;
|
||||
import haxework.gui.IView;
|
||||
import haxework.gui.GroupView;
|
||||
|
||||
class FrameSwitcher extends GroupView implements IFrameSwitcher {
|
||||
|
||||
public var current(default, null):Null<IView>;
|
||||
private var frames:Map<String, IView>;
|
||||
|
||||
public var animateFactory(default, default):Class<IAnimate>;
|
||||
private var animate:IAnimate;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
frames = new Map<String, IView>();
|
||||
current = null;
|
||||
}
|
||||
|
||||
private function buildAnimate(view:IView):Null<IAnimate> {
|
||||
if (animateFactory != null) {
|
||||
return Type.createInstance(animateFactory, [view]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function change(id:String):IView {
|
||||
var prev = null;
|
||||
if (current != null) {
|
||||
if (current.id == id) return current;
|
||||
prev = current;
|
||||
}
|
||||
current = frames.get(id);
|
||||
addView(current);
|
||||
//ToDo:
|
||||
if (content.stage != null) content.stage.focus = cast(current, SpriteView).contentAsSprite;
|
||||
var onShowMethod:Dynamic = Reflect.field(current, "onShow");
|
||||
if (onShowMethod != null) Reflect.callMethod(current, onShowMethod, []);
|
||||
if (animate != null) animate.cancel();
|
||||
animate = buildAnimate(current);
|
||||
if (animate != null && prev != null) {
|
||||
animate.start(function(_) {
|
||||
removePrev(prev);
|
||||
});
|
||||
} else {
|
||||
removePrev(prev);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
private function removePrev(prev:Null<IView>):Void {
|
||||
if (prev != null) {
|
||||
var onHideMethod:Dynamic = Reflect.field(prev, "onHide");
|
||||
if (onHideMethod != null) Reflect.callMethod(prev, onHideMethod, []);
|
||||
removeView(prev);
|
||||
}
|
||||
}
|
||||
|
||||
override public function set_views(value:Array<IView>):Array<IView> {
|
||||
views = [];
|
||||
if (value.length > 0) {
|
||||
for (view in value) {
|
||||
view.pWidth = 100;
|
||||
view.pHeight = 100;
|
||||
frames.set(view.id, view);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
8
src/main/haxework/gui/frame/IFrameSwitcher.hx
Executable file
8
src/main/haxework/gui/frame/IFrameSwitcher.hx
Executable file
@@ -0,0 +1,8 @@
|
||||
package haxework.gui.frame;
|
||||
|
||||
import haxework.gui.IView;
|
||||
|
||||
interface IFrameSwitcher extends IView {
|
||||
public var current(default, null):Null<IView>;
|
||||
public function change(id:String):IView;
|
||||
}
|
||||
79
src/main/haxework/gui/layout/DefaultLayout.hx
Executable file
79
src/main/haxework/gui/layout/DefaultLayout.hx
Executable file
@@ -0,0 +1,79 @@
|
||||
package haxework.gui.layout;
|
||||
|
||||
import haxework.gui.core.SizeType;
|
||||
import haxework.gui.core.VAlign;
|
||||
import haxework.gui.core.HAlign;
|
||||
|
||||
class DefaultLayout implements ILayout {
|
||||
|
||||
public function new() {
|
||||
|
||||
}
|
||||
|
||||
public function place(group:IGroupView, views:Array<IView>):Void {
|
||||
for (view in views) {
|
||||
setViewWidth(group, view);
|
||||
setViewHeight(group, view);
|
||||
placeViewHorizontal(group, view);
|
||||
placeViewVertical(group, view);
|
||||
}
|
||||
}
|
||||
|
||||
private function filterViews(group:IGroupView, views:Array<IView>):Array<IView> {
|
||||
return Lambda.array(Lambda.filter(views, function(view:IView):Bool {
|
||||
return if (view.inLayout) {
|
||||
true;
|
||||
} else {
|
||||
setViewWidth(group, view);
|
||||
setViewHeight(group, view);
|
||||
placeViewHorizontal(group, view);
|
||||
placeViewVertical(group, view);
|
||||
false;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private function setViewWidth(group:IGroupView, view:IView):Void {
|
||||
if (view.widthType == SizeType.PERCENT) {
|
||||
view.w = view.pWidth / 100 * (group.width - view.leftMargin - view.rightMargin - group.leftPadding - group.rightPadding);
|
||||
} else if (group.contentSize && group.width < view.width) {
|
||||
group.width = view.width;
|
||||
}
|
||||
}
|
||||
|
||||
private function setViewHeight(group:IGroupView, view:IView):Void {
|
||||
if (view.heightType == SizeType.PERCENT) {
|
||||
view.h = view.pHeight / 100 * (group.height - view.topMargin - view.bottomMargin - group.topPadding - group.bottomPadding);
|
||||
} else if (group.contentSize && group.height < view.height) {
|
||||
group.height = view.height;
|
||||
}
|
||||
}
|
||||
|
||||
private function placeViewHorizontal(group:IGroupView, view:IView):Void {
|
||||
var align:HAlign = view.hAlign;
|
||||
if (align == HAlign.NONE) align = group.layoutHAlign;
|
||||
switch (align) {
|
||||
case HAlign.LEFT:
|
||||
view.x = group.leftPadding + view.leftMargin;
|
||||
case HAlign.CENTER:
|
||||
view.x = (group.width - view.width) / 2 + (group.leftPadding - group.rightPadding) + (view.leftMargin - view.rightMargin);
|
||||
case HAlign.RIGHT:
|
||||
view.x = group.width - view.width - group.rightPadding - view.rightMargin;
|
||||
case HAlign.NONE:
|
||||
}
|
||||
}
|
||||
|
||||
private function placeViewVertical(group:IGroupView, view:IView):Void {
|
||||
var align:VAlign = view.vAlign;
|
||||
if (align == VAlign.NONE) align = group.layoutVAlign;
|
||||
switch (align) {
|
||||
case VAlign.TOP:
|
||||
view.y = group.topPadding + view.topMargin;
|
||||
case VAlign.MIDDLE:
|
||||
view.y = (group.height - view.height) / 2 + (group.topPadding - group.bottomPadding) + (view.topMargin - view.bottomMargin);
|
||||
case VAlign.BOTTOM:
|
||||
view.y = group.height - view.height - group.bottomPadding - view.bottomMargin;
|
||||
case VAlign.NONE:
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/main/haxework/gui/layout/HorizontalLayout.hx
Executable file
56
src/main/haxework/gui/layout/HorizontalLayout.hx
Executable file
@@ -0,0 +1,56 @@
|
||||
package haxework.gui.layout;
|
||||
|
||||
import haxework.core.Tuple;
|
||||
import haxework.gui.core.HAlign;
|
||||
import haxework.gui.core.SizeType;
|
||||
|
||||
class HorizontalLayout extends DefaultLayout {
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
}
|
||||
|
||||
override public function place(group:IGroupView, views:Array<IView>):Void {
|
||||
views = filterViews(group, views);
|
||||
|
||||
var fixedSize:Float = group.layoutMargin * (views.length - 1);
|
||||
var leftSize:Float = group.width - group.leftPadding - group.rightPadding;
|
||||
var maxHeight:Float = 0;
|
||||
|
||||
for (view in views) {
|
||||
switch (view.widthType) {
|
||||
case SizeType.NORMAL: fixedSize += (view.width + view.leftMargin + view.rightMargin);
|
||||
case SizeType.PERCENT: leftSize -= (view.leftMargin + view.rightMargin);
|
||||
}
|
||||
setViewHeight(group, view);
|
||||
placeViewVertical(group, view);
|
||||
maxHeight = Math.max(maxHeight, view.height);
|
||||
}
|
||||
|
||||
if (group.contentSize) {
|
||||
group.width = Math.max(group.width, fixedSize + group.leftPadding + group.rightPadding);
|
||||
group.height = maxHeight + group.topPadding + group.bottomPadding;
|
||||
}
|
||||
|
||||
leftSize -= fixedSize;
|
||||
for (view in views) {
|
||||
if (view.widthType == SizeType.PERCENT) {
|
||||
view.w = view.pWidth / 100 * leftSize;
|
||||
fixedSize += view.width + view.leftMargin + view.rightMargin;
|
||||
}
|
||||
}
|
||||
|
||||
var x:Float = 0;
|
||||
switch (group.layoutHAlign) {
|
||||
case HAlign.LEFT: x = group.leftPadding;
|
||||
case HAlign.CENTER: x = (group.width - fixedSize) / 2 + group.leftPadding - group.rightPadding;
|
||||
case HAlign.RIGHT: x = group.width - fixedSize - group.rightPadding;
|
||||
case _:
|
||||
}
|
||||
|
||||
for (view in views) {
|
||||
view.x = x + view.leftMargin;
|
||||
x += (view.width + view.leftMargin + view.rightMargin + group.layoutMargin);
|
||||
}
|
||||
}
|
||||
}
|
||||
5
src/main/haxework/gui/layout/ILayout.hx
Executable file
5
src/main/haxework/gui/layout/ILayout.hx
Executable file
@@ -0,0 +1,5 @@
|
||||
package haxework.gui.layout;
|
||||
|
||||
interface ILayout {
|
||||
public function place(group:IGroupView, views:Array<IView>):Void;
|
||||
}
|
||||
55
src/main/haxework/gui/layout/VerticalLayout.hx
Executable file
55
src/main/haxework/gui/layout/VerticalLayout.hx
Executable file
@@ -0,0 +1,55 @@
|
||||
package haxework.gui.layout;
|
||||
|
||||
import haxework.gui.core.VAlign;
|
||||
import haxework.gui.core.SizeType;
|
||||
|
||||
class VerticalLayout extends DefaultLayout {
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
}
|
||||
|
||||
override public function place(group:IGroupView, views:Array<IView>):Void {
|
||||
views = filterViews(group, views);
|
||||
|
||||
var fixedSize:Float = group.layoutMargin * (views.length - 1);
|
||||
var leftSize:Float = group.height - group.topPadding - group.bottomPadding;
|
||||
var maxWidth:Float = 0;
|
||||
|
||||
for (view in views) {
|
||||
switch (view.heightType) {
|
||||
case SizeType.NORMAL: fixedSize += (view.height + view.topMargin + view.bottomMargin);
|
||||
case SizeType.PERCENT: leftSize -= (view.topMargin + view.bottomMargin);
|
||||
}
|
||||
setViewWidth(group, view);
|
||||
placeViewHorizontal(group, view);
|
||||
maxWidth = Math.max(maxWidth, view.width);
|
||||
}
|
||||
|
||||
if (group.contentSize) {
|
||||
group.width = maxWidth + group.leftPadding + group.rightPadding;
|
||||
group.height = Math.max(group.height, fixedSize + group.topPadding + group.bottomPadding);
|
||||
}
|
||||
|
||||
leftSize -= fixedSize;
|
||||
for (view in views) {
|
||||
if (view.heightType == SizeType.PERCENT) {
|
||||
view.h = view.pHeight / 100 * leftSize;
|
||||
fixedSize += view.height + view.topMargin + view.bottomMargin;
|
||||
}
|
||||
}
|
||||
|
||||
var y:Float = 0;
|
||||
switch (group.layoutVAlign) {
|
||||
case VAlign.TOP: y = group.topPadding;
|
||||
case VAlign.MIDDLE: y = (group.height - fixedSize) / 2 + group.topPadding - group.bottomPadding;
|
||||
case VAlign.BOTTOM: y = group.height - fixedSize - group.bottomPadding;
|
||||
case _:
|
||||
}
|
||||
|
||||
for (view in views) {
|
||||
view.y = y + view.topMargin;
|
||||
y += (view.height + view.topMargin + view.bottomMargin + group.layoutMargin);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/main/haxework/gui/list/HListView.hx
Executable file
33
src/main/haxework/gui/list/HListView.hx
Executable file
@@ -0,0 +1,33 @@
|
||||
package haxework.gui.list;
|
||||
|
||||
import haxework.gui.list.ListView.IListItemView;
|
||||
import haxework.gui.core.HAlign;
|
||||
import haxework.gui.layout.VerticalLayout;
|
||||
import haxework.gui.list.HScrollView;
|
||||
import haxework.gui.core.VAlign;
|
||||
import haxework.gui.layout.HorizontalLayout;
|
||||
|
||||
class HListView<D> extends ListView<D> {
|
||||
|
||||
public function new() {
|
||||
super(new HorizontalLayout(), new VerticalLayout());
|
||||
box.layoutHAlign = HAlign.LEFT;
|
||||
box.layoutVAlign = VAlign.MIDDLE;
|
||||
}
|
||||
|
||||
override private function recalcSize(item:IListItemView<D>):Void {
|
||||
itemSize = item.width + item.leftMargin + item.rightMargin + box.layoutMargin;
|
||||
size = Math.ceil(Math.max(0, box.width / itemSize)) + 2;
|
||||
sizeDiff = size - ((box.width - box.layoutMargin - 1) / itemSize);
|
||||
}
|
||||
|
||||
override private function set_offsetDiff(value:Float):Float {
|
||||
box.leftPadding = -value * itemSize;
|
||||
mask.leftMargin = -box.leftPadding;
|
||||
return super.set_offsetDiff(value);
|
||||
}
|
||||
|
||||
override private function onMouseWheel(value:Int):Void {
|
||||
offset = offset + value;
|
||||
}
|
||||
}
|
||||
26
src/main/haxework/gui/list/HScrollSkin.hx
Executable file
26
src/main/haxework/gui/list/HScrollSkin.hx
Executable file
@@ -0,0 +1,26 @@
|
||||
package haxework.gui.list;
|
||||
|
||||
import haxework.gui.skin.ISkin;
|
||||
import flash.display.Sprite;
|
||||
import flash.display.Graphics;
|
||||
|
||||
class HScrollSkin implements ISkin<ScrollView> {
|
||||
|
||||
public var foreColor(default, default):Int;
|
||||
public var backColor(default, default):Int;
|
||||
|
||||
public function new(?foreColor:Int = 0xffffff, ?backColor:Int = 0x707070) {
|
||||
this.foreColor = foreColor;
|
||||
this.backColor = backColor;
|
||||
}
|
||||
|
||||
public function draw(view:ScrollView):Void {
|
||||
var graphics:Graphics = view.contentAsSprite.graphics;
|
||||
graphics.clear();
|
||||
graphics.beginFill(backColor);
|
||||
graphics.drawRect(0, 0, view.width, view.height);
|
||||
graphics.beginFill(foreColor);
|
||||
graphics.drawRect(view.width * view.position, 0, view.width * view.ratio, view.height);
|
||||
graphics.endFill();
|
||||
}
|
||||
}
|
||||
23
src/main/haxework/gui/list/HScrollView.hx
Executable file
23
src/main/haxework/gui/list/HScrollView.hx
Executable file
@@ -0,0 +1,23 @@
|
||||
package haxework.gui.list;
|
||||
|
||||
import haxework.gui.list.ScrollView.ScrollListener;
|
||||
import flash.geom.Point;
|
||||
import haxework.gui.list.HScrollSkin;
|
||||
|
||||
class HScrollView extends ScrollView {
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
}
|
||||
|
||||
override private function onMouseDown(p:Point):Void {
|
||||
mousePosition = p.x - width * position;
|
||||
}
|
||||
|
||||
override private function onMouseMove(p:Point):Void {
|
||||
position = (p.x - mousePosition) / width;
|
||||
dispatcher.dispatch(function(listener:ScrollListener):Void {
|
||||
listener.onScroll(position);
|
||||
});
|
||||
}
|
||||
}
|
||||
247
src/main/haxework/gui/list/ListView.hx
Executable file
247
src/main/haxework/gui/list/ListView.hx
Executable file
@@ -0,0 +1,247 @@
|
||||
package haxework.gui.list;
|
||||
|
||||
import haxework.gui.skin.ColorSkin;
|
||||
import haxework.gui.core.VAlign;
|
||||
import haxework.gui.layout.ILayout;
|
||||
import haxework.utils.NumberUtil;
|
||||
import haxework.dispath.Dispatcher;
|
||||
import haxework.dispath.IDispatcher;
|
||||
import haxework.gui.list.ScrollView.ScrollListener;
|
||||
import flash.events.MouseEvent;
|
||||
import haxework.gui.core.HAlign;
|
||||
import flash.display.Sprite;
|
||||
import haxework.gui.skin.ISkin;
|
||||
|
||||
class ListView<D> extends GroupView implements ScrollListener {
|
||||
|
||||
public var data(default, set):Array<D>;
|
||||
public var factory(null, default):Class<IListItemView<D>>;
|
||||
|
||||
public var offset(default, set):Int;
|
||||
private var offsetDiff(default, set):Float;
|
||||
|
||||
private var size(default, set):Int;
|
||||
private var sizeDiff:Float;
|
||||
|
||||
public var dispatcher(default, null):IDispatcher<ListViewListener<D>>;
|
||||
public var scroll(default, set):ScrollView;
|
||||
|
||||
public var prev(default, set):ButtonView;
|
||||
public var next(default, set):ButtonView;
|
||||
|
||||
public var filter(default, set):D->Bool;
|
||||
private var filteredData:Array<D>;
|
||||
|
||||
public var selected(default, set):Array<D>;
|
||||
|
||||
private var main:GroupView;
|
||||
private var box:GroupView;
|
||||
private var mask:SpriteView;
|
||||
private var itemSize:Float;
|
||||
|
||||
private var items:Array<IListItemView<D>>;
|
||||
private var itemsListeners:Map<IListItemView<D>, MouseEvent->Void>;
|
||||
|
||||
public function new(layout:ILayout, otherLayout:ILayout) {
|
||||
super(otherLayout);
|
||||
main = new GroupView(layout);
|
||||
main.layoutHAlign = HAlign.CENTER;
|
||||
main.layoutVAlign = VAlign.MIDDLE;
|
||||
main.pWidth = 100;
|
||||
main.pHeight = 100;
|
||||
addView(main);
|
||||
box = new GroupView(layout);
|
||||
box.pWidth = 100;
|
||||
box.pHeight = 100;
|
||||
main.addView(box);
|
||||
mask = new SpriteView();
|
||||
mask.pWidth = 100;
|
||||
mask.pHeight = 100;
|
||||
mask.inLayout = false;
|
||||
mask.skin = new ColorSkin(0xffffff);
|
||||
box.content.mask = mask.content;
|
||||
box.addView(mask);
|
||||
dispatcher = new Dispatcher<ListViewListener<D>>();
|
||||
itemSize = 0;
|
||||
offset = 0;
|
||||
offsetDiff = 0;
|
||||
sizeDiff = 0;
|
||||
items = [];
|
||||
itemsListeners = new Map<IListItemView<D>, MouseEvent->Void>();
|
||||
selected = [];
|
||||
content.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheelEvent);
|
||||
}
|
||||
|
||||
private function set_scroll(value:ScrollView):ScrollView {
|
||||
if (scroll != null) {
|
||||
scroll.dispatcher.removeListener(this);
|
||||
removeView(scroll);
|
||||
}
|
||||
scroll = value;
|
||||
if (scroll != null) {
|
||||
scroll.dispatcher.addListener(this);
|
||||
addView(scroll);
|
||||
}
|
||||
invalidate();
|
||||
return scroll;
|
||||
}
|
||||
|
||||
private function set_prev(value:ButtonView):ButtonView {
|
||||
if (prev != null) {
|
||||
main.removeView(prev);
|
||||
prev.dispose();
|
||||
}
|
||||
prev = value;
|
||||
prev.onPress = {onPress:onPrevPress};
|
||||
main.addViewFirst(prev);
|
||||
return prev;
|
||||
}
|
||||
|
||||
private function onPrevPress(_):Void {
|
||||
offset = offset - 1;
|
||||
}
|
||||
|
||||
private function set_next(value:ButtonView):ButtonView {
|
||||
if (next != null) {
|
||||
main.removeView(next);
|
||||
next.dispose();
|
||||
}
|
||||
next = value;
|
||||
next.onPress = {onPress:onNextPress};
|
||||
main.addView(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
private function onNextPress(_):Void {
|
||||
offset = offset + 1;
|
||||
}
|
||||
|
||||
public function onScroll(position:Float):Void {
|
||||
var x:Float = filteredData.length * position;
|
||||
var o:Int = Math.floor(x);
|
||||
offsetDiff = (x - o);
|
||||
offset = o;
|
||||
}
|
||||
|
||||
private function onMouseWheelEvent(event:MouseEvent):Void {
|
||||
#if flash event.preventDefault(); #end
|
||||
onMouseWheel(event.delta);
|
||||
}
|
||||
|
||||
private function onMouseWheel(value:Int):Void {}
|
||||
|
||||
private function set_offset(value:Int):Int {
|
||||
value = NumberUtil.limitInt(value, 0, filteredData == null ? 0 : filteredData.length - size + 2);
|
||||
if (offset != value) {
|
||||
if (filteredData != null) {
|
||||
//ToDo: constant for 2
|
||||
if (value == 0) offsetDiff = 0;
|
||||
if (value == filteredData.length - size + 2) offsetDiff = sizeDiff - 2;
|
||||
}
|
||||
offset = value;
|
||||
render();
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private function set_data(value:Array<D>):Array<D> {
|
||||
data = value;
|
||||
render();
|
||||
return data;
|
||||
}
|
||||
|
||||
private function set_filter(value:D->Bool):D->Bool {
|
||||
if (filter != value) {
|
||||
filter = value;
|
||||
render();
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
private function set_selected(value:Array<D>):Array<D> {
|
||||
if (selected != value) {
|
||||
selected = value;
|
||||
invalidate();
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
public function render():Void {
|
||||
if (data != null && factory != null) {
|
||||
filteredData = filter == null ? data : data.filter(filter);
|
||||
scroll.ratio = Math.min(1.0, (size - sizeDiff) / filteredData.length);
|
||||
scroll.position = ((offset + offsetDiff) / filteredData.length);
|
||||
for (i in 0...size) {
|
||||
var item:IListItemView<D> = items[i];
|
||||
var index = offset + i;
|
||||
if (filteredData[index] == null) {
|
||||
item.visible = false;
|
||||
} else {
|
||||
item.visible = true;
|
||||
item.item_index = index;
|
||||
item.data = filteredData[item.item_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public function update():Void {
|
||||
super.update();
|
||||
recalcSize(Type.createInstance(factory, []));
|
||||
render();
|
||||
}
|
||||
|
||||
private function recalcSize(item:IListItemView<D>):Void {}
|
||||
|
||||
private function set_size(value:Int):Int {
|
||||
if (size != value) {
|
||||
size = value;
|
||||
var diff:Int = size - items.length;
|
||||
if (diff > 0) {
|
||||
for (i in 0...diff) {
|
||||
var item:IListItemView<D> = Type.createInstance(factory, []);
|
||||
items.push(item);
|
||||
setClickListener(item);
|
||||
box.addView(item);
|
||||
}
|
||||
} else if (diff < 0) {
|
||||
for (i in 0...-diff) {
|
||||
var item:IListItemView<D> = items.pop();
|
||||
item.content.removeEventListener(MouseEvent.CLICK, itemsListeners.get(item));
|
||||
itemsListeners.remove(item);
|
||||
box.removeView(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private function set_offsetDiff(value:Float):Float {
|
||||
offsetDiff = value;
|
||||
return offsetDiff;
|
||||
}
|
||||
|
||||
private function setClickListener(item:IListItemView<D>):Void {
|
||||
var listener:MouseEvent->Void = function(event:MouseEvent):Void {
|
||||
dispatcher.dispatch(function(listener:ListViewListener<D>):Void {
|
||||
listener.onListItemClick(item);
|
||||
});
|
||||
}
|
||||
item.content.addEventListener(MouseEvent.CLICK, listener);
|
||||
itemsListeners.set(item, listener);
|
||||
}
|
||||
|
||||
override private function set_layoutMargin(value:Float):Float {
|
||||
return box.layoutMargin = value;
|
||||
}
|
||||
}
|
||||
|
||||
interface IListItemView<D> extends IView {
|
||||
public var item_index(default, default):Int;
|
||||
public var data(default, set):D;
|
||||
}
|
||||
|
||||
interface ListViewListener<D> {
|
||||
public function onListItemClick(item:IListItemView<D>):Void;
|
||||
}
|
||||
|
||||
68
src/main/haxework/gui/list/ScrollView.hx
Executable file
68
src/main/haxework/gui/list/ScrollView.hx
Executable file
@@ -0,0 +1,68 @@
|
||||
package haxework.gui.list;
|
||||
|
||||
import haxework.utils.NumberUtil;
|
||||
import flash.geom.Point;
|
||||
import haxework.dispath.Dispatcher;
|
||||
import haxework.dispath.IDispatcher;
|
||||
import flash.events.MouseEvent;
|
||||
|
||||
class ScrollView extends SpriteView {
|
||||
|
||||
public var position(default, set):Float;
|
||||
public var ratio(default, set):Float;
|
||||
|
||||
public var dispatcher(default, null):IDispatcher<ScrollListener>;
|
||||
|
||||
private var mousePosition:Float;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
contentAsSprite.buttonMode = true;
|
||||
position = 0;
|
||||
ratio = 1;
|
||||
dispatcher = new Dispatcher<ScrollListener>();
|
||||
content.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownEvent);
|
||||
}
|
||||
|
||||
private function onMouseDownEvent(event:MouseEvent):Void {
|
||||
content.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveEvent);
|
||||
content.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUpEvent);
|
||||
var p:Point = content.globalToLocal(new Point(event.stageX, event.stageY));
|
||||
onMouseDown(p);
|
||||
}
|
||||
|
||||
private function onMouseMoveEvent(event:MouseEvent):Void {
|
||||
var p:Point = content.globalToLocal(new Point(event.stageX, event.stageY));
|
||||
onMouseMove(p);
|
||||
}
|
||||
|
||||
private function onMouseUpEvent(event:MouseEvent):Void {
|
||||
content.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveEvent);
|
||||
content.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUpEvent);
|
||||
}
|
||||
|
||||
private function onMouseDown(p:Point):Void {}
|
||||
|
||||
private function onMouseMove(p:Point):Void {}
|
||||
|
||||
private function set_position(value:Float):Float {
|
||||
value = NumberUtil.limitFloat(value, 0, 1 - ratio);
|
||||
if (position != value) {
|
||||
position = value;
|
||||
invalidate();
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
private function set_ratio(value:Float):Float {
|
||||
if (ratio != value) {
|
||||
ratio = value;
|
||||
invalidate();
|
||||
}
|
||||
return ratio;
|
||||
}
|
||||
}
|
||||
|
||||
interface ScrollListener {
|
||||
public function onScroll(position:Float):Void;
|
||||
}
|
||||
33
src/main/haxework/gui/list/VListView.hx
Executable file
33
src/main/haxework/gui/list/VListView.hx
Executable file
@@ -0,0 +1,33 @@
|
||||
package haxework.gui.list;
|
||||
|
||||
import haxework.gui.list.ListView.IListItemView;
|
||||
import haxework.gui.core.VAlign;
|
||||
import haxework.gui.layout.VerticalLayout;
|
||||
import haxework.gui.list.VScrollView;
|
||||
import haxework.gui.core.HAlign;
|
||||
import haxework.gui.layout.HorizontalLayout;
|
||||
|
||||
class VListView<D> extends ListView<D> {
|
||||
|
||||
public function new() {
|
||||
super(new VerticalLayout(), new HorizontalLayout());
|
||||
box.layoutHAlign = HAlign.CENTER;
|
||||
box.layoutVAlign = VAlign.TOP;
|
||||
}
|
||||
|
||||
override private function recalcSize(item:IListItemView<D>):Void {
|
||||
itemSize = item.height + item.topMargin + item.bottomMargin + box.layoutMargin;
|
||||
size = Math.ceil(Math.max(0, box.height / itemSize)) + 2;
|
||||
sizeDiff = size - ((box.height - box.layoutMargin - 1) / itemSize);
|
||||
}
|
||||
|
||||
override private function set_offsetDiff(value:Float):Float {
|
||||
box.topPadding = -value * itemSize;
|
||||
mask.topMargin = -box.topPadding;
|
||||
return super.set_offsetDiff(value);
|
||||
}
|
||||
|
||||
override private function onMouseWheel(value:Int):Void {
|
||||
offset = offset - value;
|
||||
}
|
||||
}
|
||||
26
src/main/haxework/gui/list/VScrollSkin.hx
Executable file
26
src/main/haxework/gui/list/VScrollSkin.hx
Executable file
@@ -0,0 +1,26 @@
|
||||
package haxework.gui.list;
|
||||
|
||||
import haxework.gui.skin.ISkin;
|
||||
import flash.display.Sprite;
|
||||
import flash.display.Graphics;
|
||||
|
||||
class VScrollSkin implements ISkin<ScrollView> {
|
||||
|
||||
public var foreColor(default, default):Int;
|
||||
public var backColor(default, default):Int;
|
||||
|
||||
public function new(?foreColor:Int = 0xffffff, ?backColor:Int = 0x707070) {
|
||||
this.foreColor = foreColor;
|
||||
this.backColor = backColor;
|
||||
}
|
||||
|
||||
public function draw(view:ScrollView):Void {
|
||||
var graphics:Graphics = view.contentAsSprite.graphics;
|
||||
graphics.clear();
|
||||
graphics.beginFill(backColor);
|
||||
graphics.drawRect(0, 0, view.width, view.height);
|
||||
graphics.beginFill(foreColor);
|
||||
graphics.drawRect(0, view.height * view.position, view.width, view.height * view.ratio);
|
||||
graphics.endFill();
|
||||
}
|
||||
}
|
||||
23
src/main/haxework/gui/list/VScrollView.hx
Executable file
23
src/main/haxework/gui/list/VScrollView.hx
Executable file
@@ -0,0 +1,23 @@
|
||||
package haxework.gui.list;
|
||||
|
||||
import haxework.gui.list.ScrollView.ScrollListener;
|
||||
import flash.geom.Point;
|
||||
import haxework.gui.list.VScrollSkin;
|
||||
|
||||
class VScrollView extends ScrollView {
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
}
|
||||
|
||||
override private function onMouseDown(p:Point):Void {
|
||||
mousePosition = p.y - height * position;
|
||||
}
|
||||
|
||||
override private function onMouseMove(p:Point):Void {
|
||||
position = (p.y - mousePosition) / height;
|
||||
dispatcher.dispatch(function(listener:ScrollListener):Void {
|
||||
listener.onScroll(position);
|
||||
});
|
||||
}
|
||||
}
|
||||
47
src/main/haxework/gui/popup/PopupManager.hx
Executable file
47
src/main/haxework/gui/popup/PopupManager.hx
Executable file
@@ -0,0 +1,47 @@
|
||||
package haxework.gui.popup;
|
||||
|
||||
import haxework.animate.IAnimate;
|
||||
import haxework.gui.Root;
|
||||
import haxework.gui.IGroupView;
|
||||
|
||||
class PopupManager {
|
||||
|
||||
public var showAnimateFactory(default, default):Class<IAnimate>;
|
||||
public var closeAnimateFactory(default, default):Class<IAnimate>;
|
||||
|
||||
private var popups:Array<PopupView<Dynamic>>;
|
||||
|
||||
public function new() {
|
||||
popups = new Array<PopupView<Dynamic>>();
|
||||
}
|
||||
|
||||
public function show(popup:PopupView<Dynamic>):Void {
|
||||
cast(Root.instance.view, IGroupView).addView(popup);
|
||||
if (showAnimateFactory != null) {
|
||||
Type.createInstance(showAnimateFactory, [popup]).start(null);
|
||||
}
|
||||
popups.push(popup);
|
||||
popup.onShow();
|
||||
}
|
||||
|
||||
public function close(popup:PopupView<Dynamic>):Void {
|
||||
popups.remove(popup);
|
||||
if (closeAnimateFactory != null) {
|
||||
Type.createInstance(closeAnimateFactory, [popup]).start(function(_) {
|
||||
cast(Root.instance.view, IGroupView).removeView(popup);
|
||||
popup.onClose();
|
||||
});
|
||||
} else {
|
||||
cast(Root.instance.view, IGroupView).removeView(popup);
|
||||
popup.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
public function closeTop():Bool {
|
||||
if (popups.length > 0) {
|
||||
close(popups[popups.length - 1]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
57
src/main/haxework/gui/popup/PopupView.hx
Executable file
57
src/main/haxework/gui/popup/PopupView.hx
Executable file
@@ -0,0 +1,57 @@
|
||||
package haxework.gui.popup;
|
||||
|
||||
import haxework.net.callback.Callback;
|
||||
import haxework.net.callback.ICallback;
|
||||
import haxe.Timer;
|
||||
import haxework.provider.Provider;
|
||||
import haxework.dispath.Dispatcher;
|
||||
import haxework.dispath.IDispatcher;
|
||||
import haxework.gui.IGroupView;
|
||||
import haxework.gui.ButtonView;
|
||||
import haxework.gui.skin.ColorSkin;
|
||||
import haxework.gui.GroupView;
|
||||
|
||||
class PopupView<V:IView> extends GroupView {
|
||||
|
||||
private var buttonId:String;
|
||||
private var contentView:V;
|
||||
private var callback:ICallback<String>;
|
||||
|
||||
public function new(contentViewFactory:Class<V>) {
|
||||
super();
|
||||
|
||||
pWidth = 100;
|
||||
pHeight = 100;
|
||||
inLayout = false;
|
||||
skin = new ColorSkin(0x000000, 0.6);
|
||||
|
||||
this.contentView = Type.createInstance(contentViewFactory, [{listener:this}]);
|
||||
addView(contentView);
|
||||
}
|
||||
|
||||
public function onPress(button:ButtonView) {
|
||||
this.buttonId = button.id;
|
||||
close();
|
||||
}
|
||||
|
||||
public function show():ICallback<String> {
|
||||
Provider.get(PopupManager).show(this);
|
||||
callback = Callback.build();
|
||||
return callback;
|
||||
}
|
||||
|
||||
public function close():Void {
|
||||
Provider.get(PopupManager).close(this);
|
||||
}
|
||||
|
||||
public function onShow():Void {
|
||||
buttonId = "close";
|
||||
}
|
||||
|
||||
public function onClose():Void {
|
||||
if (callback != null) {
|
||||
callback.callSuccess(buttonId);
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/main/haxework/gui/skin/BitmapSkin.hx
Executable file
43
src/main/haxework/gui/skin/BitmapSkin.hx
Executable file
@@ -0,0 +1,43 @@
|
||||
package haxework.gui.skin;
|
||||
|
||||
import flash.geom.Rectangle;
|
||||
import flash.geom.Matrix;
|
||||
import flash.display.BitmapData;
|
||||
import haxework.gui.utils.ColorUtils;
|
||||
import haxework.gui.utils.DrawUtil;
|
||||
import haxework.gui.ButtonView.ButtonState;
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
|
||||
class BitmapSkin implements ISkin<SpriteView> implements ISize {
|
||||
|
||||
public var width(default, null):Float;
|
||||
public var height(default, null):Float;
|
||||
|
||||
public var image(null, set):BitmapData;
|
||||
public var color(default, default):Int;
|
||||
public var fillType(default, default):FillType;
|
||||
|
||||
public function new(?image:BitmapData = null, ?fillType = null, ?color = -1) {
|
||||
if (image != null) {
|
||||
this.image = image;
|
||||
}
|
||||
this.fillType = fillType;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
private function set_image(value:BitmapData):BitmapData {
|
||||
if (image != value) {
|
||||
image = value;
|
||||
width = image.width;
|
||||
height = image.height;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
public function draw(view:SpriteView):Void {
|
||||
if (image == null) return;
|
||||
DrawUtil.draw(view.contentAsSprite.graphics, image, new Rectangle(0, 0, view.width, view.height), fillType, color);
|
||||
}
|
||||
|
||||
}
|
||||
70
src/main/haxework/gui/skin/ButtonBitmapSkin.hx
Executable file
70
src/main/haxework/gui/skin/ButtonBitmapSkin.hx
Executable file
@@ -0,0 +1,70 @@
|
||||
package haxework.gui.skin;
|
||||
|
||||
import haxework.gui.utils.BitmapUtil;
|
||||
import haxework.gui.utils.DrawUtil;
|
||||
import flash.geom.Rectangle;
|
||||
import flash.display.BitmapData;
|
||||
import haxework.gui.utils.ColorUtils;
|
||||
import haxework.gui.ButtonView.ButtonState;
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
|
||||
class ButtonBitmapSkin implements ISkin<ButtonView> implements ISize {
|
||||
|
||||
public var width(default, null):Float;
|
||||
public var height(default, null):Float;
|
||||
|
||||
public var fillType(default, default):FillType;
|
||||
public var color(default, default):Int;
|
||||
public var image(null, set):BitmapData;
|
||||
public var upImage(null, set):BitmapData;
|
||||
public var overImage(null, set):BitmapData;
|
||||
public var downImage(null, set):BitmapData;
|
||||
public var disableImage(null, default):BitmapData;
|
||||
|
||||
private var images:Map<ButtonState, BitmapData>;
|
||||
private var disable:BitmapData;
|
||||
|
||||
public function new(?image:BitmapData = null, ?fillType:FillType = null, ?color = -1) {
|
||||
images = new Map<ButtonState, BitmapData>();
|
||||
if (image != null) {
|
||||
this.image = image;
|
||||
}
|
||||
this.fillType = fillType;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
private function set_image(value:BitmapData):BitmapData {
|
||||
width = value.width;
|
||||
height = value.height;
|
||||
images.set(ButtonState.UP, value);
|
||||
images.set(ButtonState.DOWN, BitmapUtil.multiply(value, 0.8));
|
||||
images.set(ButtonState.OVER, BitmapUtil.multiply(value, 1.2));
|
||||
disable = BitmapUtil.grayscale(value, 0.2);
|
||||
return value;
|
||||
}
|
||||
|
||||
private function set_upImage(value:BitmapData):BitmapData {
|
||||
width = value.width;
|
||||
height = value.height;
|
||||
images.set(ButtonState.UP, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private function set_overImage(value:BitmapData):BitmapData {
|
||||
images.set(ButtonState.OVER, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private function set_downImage(value:BitmapData):BitmapData {
|
||||
images.set(ButtonState.DOWN, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public function draw(view:ButtonView):Void {
|
||||
if (images == null) return;
|
||||
var image:BitmapData = view.disabled ? disableImage == null ? disable : disableImage : images.get(view.state);
|
||||
DrawUtil.draw(view.contentAsSprite.graphics, image, new Rectangle(0, 0, view.width, view.height), fillType, color);
|
||||
}
|
||||
|
||||
}
|
||||
41
src/main/haxework/gui/skin/ButtonColorSkin.hx
Executable file
41
src/main/haxework/gui/skin/ButtonColorSkin.hx
Executable file
@@ -0,0 +1,41 @@
|
||||
package haxework.gui.skin;
|
||||
|
||||
import haxework.gui.utils.ColorUtils;
|
||||
import haxework.gui.ButtonView.ButtonState;
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
|
||||
class ButtonColorSkin implements ISkin<ButtonView> {
|
||||
|
||||
public var color(default, set_color):Int;
|
||||
public var alpha(default, default):Float;
|
||||
public var disable(default, default):Int;
|
||||
private var colors:Map<ButtonState, Int>;
|
||||
|
||||
public function new(?color:Int = 0xffffff, ?alpha:Float = 1.0) {
|
||||
this.color = color;
|
||||
this.alpha = alpha;
|
||||
}
|
||||
|
||||
private function set_color(value:Int):Int {
|
||||
colors = new Map<ButtonState, Int>();
|
||||
colors.set(ButtonState.UP, value);
|
||||
colors.set(ButtonState.DOWN, ColorUtils.diff(value, -64));
|
||||
colors.set(ButtonState.OVER, ColorUtils.diff(value, 64));
|
||||
//disable = ColorUtils.multiply(value, 0.6);
|
||||
return value;
|
||||
}
|
||||
|
||||
public function draw(view:ButtonView):Void {
|
||||
var color:Int = selectColor(view);
|
||||
var graphics:Graphics = view.contentAsSprite.graphics;
|
||||
graphics.clear();
|
||||
graphics.beginFill(color, alpha);
|
||||
graphics.drawRect(0, 0, view.width, view.height);
|
||||
graphics.endFill();
|
||||
}
|
||||
|
||||
private function selectColor(view:ButtonView):Int {
|
||||
return view.disabled ? disable : colors.get(view.state);
|
||||
}
|
||||
}
|
||||
24
src/main/haxework/gui/skin/ColorSkin.hx
Executable file
24
src/main/haxework/gui/skin/ColorSkin.hx
Executable file
@@ -0,0 +1,24 @@
|
||||
package haxework.gui.skin;
|
||||
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
|
||||
class ColorSkin implements ISkin<SpriteView> {
|
||||
|
||||
public var color(default, default):Int;
|
||||
public var alpha(default, default):Float;
|
||||
|
||||
public function new(?color:Int = 0x000000, ?alpha:Float = 1.0) {
|
||||
this.color = color;
|
||||
this.alpha = alpha;
|
||||
}
|
||||
|
||||
public function draw(view:SpriteView):Void {
|
||||
var graphics:Graphics = view.contentAsSprite.graphics;
|
||||
graphics.clear();
|
||||
graphics.beginFill(color, alpha);
|
||||
graphics.drawRect(0, 0, view.width, view.height);
|
||||
graphics.endFill();
|
||||
}
|
||||
|
||||
}
|
||||
19
src/main/haxework/gui/skin/FakeSkin.hx
Executable file
19
src/main/haxework/gui/skin/FakeSkin.hx
Executable file
@@ -0,0 +1,19 @@
|
||||
package haxework.gui.skin;
|
||||
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
|
||||
class FakeSkin implements ISkin<IView> {
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function draw(view:IView):Void {
|
||||
/*var g:Graphics = view.content.graphics;
|
||||
g.clear();
|
||||
g.lineStyle(1, 0x00ff00);
|
||||
g.drawRect(0, 0, view.width, view.height);
|
||||
g.endFill();
|
||||
g.lineStyle();*/
|
||||
}
|
||||
|
||||
}
|
||||
6
src/main/haxework/gui/skin/ISize.hx
Executable file
6
src/main/haxework/gui/skin/ISize.hx
Executable file
@@ -0,0 +1,6 @@
|
||||
package haxework.gui.skin;
|
||||
|
||||
interface ISize {
|
||||
public var width(default, null):Float;
|
||||
public var height(default, null):Float;
|
||||
}
|
||||
7
src/main/haxework/gui/skin/ISkin.hx
Executable file
7
src/main/haxework/gui/skin/ISkin.hx
Executable file
@@ -0,0 +1,7 @@
|
||||
package haxework.gui.skin;
|
||||
|
||||
import haxework.gui.IView.Content;
|
||||
|
||||
interface ISkin<V:IView> {
|
||||
public function draw(view:V):Void;
|
||||
}
|
||||
23
src/main/haxework/gui/skin/ProgressSkin.hx
Executable file
23
src/main/haxework/gui/skin/ProgressSkin.hx
Executable file
@@ -0,0 +1,23 @@
|
||||
package haxework.gui.skin;
|
||||
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
import haxework.gui.skin.ISkin;
|
||||
|
||||
class ProgressSkin implements ISkin<ProgressView> {
|
||||
|
||||
public var foreColor:Int;
|
||||
public var backColor:Int;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function draw(view:ProgressView):Void {
|
||||
var graphics:Graphics = view.contentAsSprite.graphics;
|
||||
graphics.clear();
|
||||
graphics.beginFill(backColor);
|
||||
graphics.drawRect(0, 0, view.width, view.height);
|
||||
graphics.beginFill(foreColor);
|
||||
graphics.drawRect(0, 0, view.width * (view.max > 0 ? view.value / view.max : 0), view.height);
|
||||
graphics.endFill();
|
||||
}
|
||||
}
|
||||
46
src/main/haxework/gui/utils/BitmapUtil.hx
Executable file
46
src/main/haxework/gui/utils/BitmapUtil.hx
Executable file
@@ -0,0 +1,46 @@
|
||||
package haxework.gui.utils;
|
||||
|
||||
import flash.geom.Point;
|
||||
import flash.filters.ColorMatrixFilter;
|
||||
import flash.geom.ColorTransform;
|
||||
import flash.display.BitmapData;
|
||||
|
||||
class BitmapUtil {
|
||||
|
||||
private static var cache:Map<BitmapData, Map<String, BitmapData>> = new Map<BitmapData, Map<String, BitmapData>>();
|
||||
|
||||
private static function fromCache(image:BitmapData, key:String):Null<BitmapData> {
|
||||
return cache.exists(image) && cache.get(image).exists(key) ? cache.get(image).get(key) : null;
|
||||
}
|
||||
|
||||
private static function toCache(image:BitmapData, key:String, value:BitmapData):Void {
|
||||
if (!cache.exists(image)) cache.set(image, new Map<String, BitmapData>());
|
||||
cache.get(image).set(key, value);
|
||||
}
|
||||
|
||||
|
||||
public static function multiply(image:BitmapData, m:Float):BitmapData {
|
||||
var result = fromCache(image, "multiply:" + m);
|
||||
if (result != null) return result;
|
||||
var ct:ColorTransform = new ColorTransform(m, m, m, 1.0, 0, 0, 0);
|
||||
var out:BitmapData = image.clone();
|
||||
out.colorTransform(out.rect, ct);
|
||||
toCache(image, "multiply:" + m, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
public static function grayscale(image:BitmapData, m:Float):BitmapData {
|
||||
var result = fromCache(image, "grayscale:" + m);
|
||||
if (result != null) return result;
|
||||
var matrix:Array<Float> = [];
|
||||
matrix = matrix.concat([m, m, m, 0, 0]);
|
||||
matrix = matrix.concat([m, m, m, 0, 0]);
|
||||
matrix = matrix.concat([m, m, m, 0, 0]);
|
||||
matrix = matrix.concat([0, 0, 0, 1, 0]);
|
||||
var cmf:ColorMatrixFilter = new ColorMatrixFilter(matrix);
|
||||
var out:BitmapData = image.clone();
|
||||
out.applyFilter(out, out.rect, new Point(0,0), cmf);
|
||||
toCache(image, "grayscale:" + m, out);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
31
src/main/haxework/gui/utils/ColorUtils.hx
Executable file
31
src/main/haxework/gui/utils/ColorUtils.hx
Executable file
@@ -0,0 +1,31 @@
|
||||
package haxework.gui.utils;
|
||||
|
||||
class ColorUtils {
|
||||
|
||||
public static function multiply(color:Int, m:Float):Int {
|
||||
var rgb:Array<Int> = color2rgb(color);
|
||||
var red:Int = cast Math.min(255, Math.round(rgb[0] * m));
|
||||
var green:Int = cast Math.min(255, Math.round(rgb[1] * m));
|
||||
var blue:Int = cast Math.min(255, Math.round(rgb[2] * m));
|
||||
return rgb2color(red, green, blue);
|
||||
}
|
||||
|
||||
public static function diff(color:Int, d:Int):Int {
|
||||
var rgb:Array<Int> = color2rgb(color);
|
||||
var red:Int = cast Math.max(0, Math.min(255, rgb[0] + d));
|
||||
var green:Int = cast Math.max(0, Math.min(255, rgb[1] + d));
|
||||
var blue:Int = cast Math.max(0, Math.min(255, rgb[2] + d));
|
||||
return rgb2color(red, green, blue);
|
||||
}
|
||||
|
||||
public static function rgb2color(red:Int, green:Int, blue:Int):Int {
|
||||
return (red << 16) + (green << 8) + blue;
|
||||
}
|
||||
|
||||
public static function color2rgb(color:Int):Array<Int> {
|
||||
var red:Int = ((color & 0xFF0000) >>> 16);
|
||||
var green:Int = ((color & 0x00FF00) >> 8);
|
||||
var blue:Int = (color & 0x0000FF);
|
||||
return [red, green, blue];
|
||||
}
|
||||
}
|
||||
130
src/main/haxework/gui/utils/DrawUtil.hx
Executable file
130
src/main/haxework/gui/utils/DrawUtil.hx
Executable file
@@ -0,0 +1,130 @@
|
||||
package haxework.gui.utils;
|
||||
|
||||
import flash.display.Bitmap;
|
||||
import flash.Lib;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Rectangle;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.Graphics;
|
||||
|
||||
@:enum abstract FillType(String) from String to String {
|
||||
var NONE = "NONE";
|
||||
var DEFAULT = "DEFAULT";
|
||||
var COVER = "COVER";
|
||||
var CONTAIN = "CONTAIN";
|
||||
var REPEAT = "REPEAT";
|
||||
var STRETCH = "STRETCH";
|
||||
var NINEPATH = "NINEPATH";
|
||||
}
|
||||
|
||||
class DrawUtil {
|
||||
|
||||
public static function draw(graphics:Graphics, image:BitmapData, rect:Rectangle, ?fillType:FillType = null, ?color:Int = -1, ?clear:Bool = true):Void {
|
||||
if (image == null) return;
|
||||
if (fillType == null) fillType = FillType.DEFAULT;
|
||||
if (clear) graphics.clear();
|
||||
if (color > -1) {
|
||||
graphics.beginFill(color);
|
||||
graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
|
||||
graphics.endFill();
|
||||
}
|
||||
var m:Matrix = new Matrix();
|
||||
var sx:Float = 1.0;
|
||||
var sy:Float = 1.0;
|
||||
switch (fillType) {
|
||||
case FillType.REPEAT:
|
||||
graphics.beginBitmapFill(image, m, true, false);
|
||||
graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
|
||||
graphics.endFill();
|
||||
return;
|
||||
case FillType.NONE:
|
||||
graphics.beginBitmapFill(image, m, false, false);
|
||||
graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
|
||||
graphics.endFill();
|
||||
return;
|
||||
case FillType.NINEPATH:
|
||||
draw9path(graphics, image, rect);
|
||||
return;
|
||||
case FillType.DEFAULT:
|
||||
case FillType.CONTAIN:
|
||||
sx = sy = Math.min(rect.width / image.width, rect.height / image.height);
|
||||
case FillType.COVER:
|
||||
sx = sy = Math.max(rect.width / image.width, rect.height / image.height);
|
||||
case FillType.STRETCH:
|
||||
sx = rect.width / image.width;
|
||||
sy = rect.height / image.height;
|
||||
}
|
||||
m.scale(sx, sy);
|
||||
var dx:Float = (rect.width - image.width * sx) / 2;
|
||||
var dy:Float = (rect.height - image.height * sy) / 2;
|
||||
m.translate(dx, dy);
|
||||
graphics.beginBitmapFill(image, m, false, true);
|
||||
rect.x = Math.max(rect.x, m.tx);
|
||||
rect.y = Math.max(rect.y, m.ty);
|
||||
rect.width = Math.min(rect.width, image.width * sx);
|
||||
rect.height = Math.min(rect.height, image.height * sy);
|
||||
graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
|
||||
graphics.endFill();
|
||||
}
|
||||
|
||||
private static function draw9path(graphics:Graphics, image:BitmapData, rect:Rectangle):Void {
|
||||
var w:Int = Math.round(image.width / 2);
|
||||
var h:Int = Math.round(image.height / 2);
|
||||
var m:Matrix = null;
|
||||
//lt
|
||||
graphics.beginBitmapFill(image, m, false);
|
||||
graphics.drawRect(0, 0, w, h);
|
||||
graphics.endFill();
|
||||
//rt
|
||||
m = new Matrix();
|
||||
m.translate(rect.width - 2 * w, 0);
|
||||
graphics.beginBitmapFill(image, m, false);
|
||||
graphics.drawRect(rect.width - w, 0, w, h);
|
||||
graphics.endFill();
|
||||
//lb
|
||||
m = new Matrix();
|
||||
m.translate(0, rect.height - 2 * h);
|
||||
graphics.beginBitmapFill(image, m, false);
|
||||
graphics.drawRect(0, rect.height - h, w, h);
|
||||
graphics.endFill();
|
||||
//rb
|
||||
m = new Matrix();
|
||||
m.translate(rect.width - 2 * w, rect.height - 2 * h);
|
||||
graphics.beginBitmapFill(image, m, false);
|
||||
graphics.drawRect(rect.width - w, rect.height - h, w, h);
|
||||
graphics.endFill();
|
||||
//c
|
||||
graphics.beginFill(image.getPixel(w, h));
|
||||
graphics.drawRect(w - 1, h - 1, rect.width - 2 * w + 2, rect.height - 2 * h + 2);
|
||||
graphics.endFill();
|
||||
//t
|
||||
var t:BitmapData = new BitmapData(1, h);
|
||||
t.copyPixels(image, new Rectangle(w, 0, 1, h), new Point(0, 0));
|
||||
graphics.beginBitmapFill(t, null, true);
|
||||
graphics.drawRect(w, 0, rect.width - w * 2, h);
|
||||
graphics.endFill();
|
||||
//b
|
||||
var b:BitmapData = new BitmapData(1, h);
|
||||
b.copyPixels(image, new Rectangle(w, h, 1, h), new Point(0, 0));
|
||||
m = new Matrix();
|
||||
m.translate(0, rect.height - h);
|
||||
graphics.beginBitmapFill(b, m, true);
|
||||
graphics.drawRect(w, rect.height - h, rect.width - w * 2, h - 2); //ToDo:?
|
||||
graphics.endFill();
|
||||
//l
|
||||
var l:BitmapData = new BitmapData(w, 1);
|
||||
l.copyPixels(image, new Rectangle(0, h, w, 1), new Point(0, 0));
|
||||
graphics.beginBitmapFill(l, null, true);
|
||||
graphics.drawRect(0, h, w, rect.height - h * 2);
|
||||
graphics.endFill();
|
||||
//r
|
||||
var r:BitmapData = new BitmapData(w, 1);
|
||||
r.copyPixels(image, new Rectangle(w, h, w, 1), new Point(0, 0));
|
||||
m = new Matrix();
|
||||
m.translate(rect.width - w, 0);
|
||||
graphics.beginBitmapFill(r, m, true);
|
||||
graphics.drawRect(rect.width - w, h, w, rect.height - h * 2);
|
||||
graphics.endFill();
|
||||
}
|
||||
}
|
||||
9
src/main/haxework/locale/ILocale.hx
Executable file
9
src/main/haxework/locale/ILocale.hx
Executable file
@@ -0,0 +1,9 @@
|
||||
package haxework.locale;
|
||||
|
||||
interface ILocale {
|
||||
public function getString(key:String):String;
|
||||
public function formatString(key:String, args:Array<Dynamic>):String;
|
||||
public function formatPlurar(key:String, num:Int, args:Array<Dynamic>):String;
|
||||
public function getArray(key:String):Array<String>;
|
||||
public function getPlural(key:String, num:Int):String;
|
||||
}
|
||||
38
src/main/haxework/locale/LString.hx
Normal file
38
src/main/haxework/locale/LString.hx
Normal file
@@ -0,0 +1,38 @@
|
||||
package haxework.locale;
|
||||
|
||||
import haxework.provider.Provider;
|
||||
|
||||
abstract LString(String) from String to String {
|
||||
|
||||
inline public function new(value:String) {
|
||||
this = Provider.get(ILocale).getString(value);
|
||||
}
|
||||
}
|
||||
|
||||
abstract LAString(String) from String to String {
|
||||
|
||||
inline public function new(value:String, index:Int) {
|
||||
this = Provider.get(ILocale).getArray(value)[index];
|
||||
}
|
||||
}
|
||||
|
||||
abstract LPString(String) from String to String {
|
||||
|
||||
inline public function new(value:String, num:Int) {
|
||||
this = Provider.get(ILocale).getPlural(value, num);
|
||||
}
|
||||
}
|
||||
|
||||
abstract LFString(String) from String to String {
|
||||
|
||||
inline public function new(value:String, args:Array<Dynamic>) {
|
||||
this = Provider.get(ILocale).formatString(value, args);
|
||||
}
|
||||
}
|
||||
|
||||
abstract LPFString(String) from String to String {
|
||||
|
||||
inline public function new(value:String, num:Int, args:Array<Dynamic>) {
|
||||
this = Provider.get(ILocale).formatPlurar(value, num, args);
|
||||
}
|
||||
}
|
||||
80
src/main/haxework/locale/Locale.hx
Executable file
80
src/main/haxework/locale/Locale.hx
Executable file
@@ -0,0 +1,80 @@
|
||||
package haxework.locale;
|
||||
|
||||
class Locale implements ILocale {
|
||||
|
||||
private var data:Dynamic;
|
||||
private var lang:String;
|
||||
|
||||
public function new(lang:String, data:Dynamic) {
|
||||
this.lang = lang;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public function formatString(key:String, args:Array<Dynamic>):String {
|
||||
var string:String = getString(key);
|
||||
for (i in 0...args.length) {
|
||||
var arg:Dynamic = args[i];
|
||||
string = StringTools.replace(string, "{" + i + "}", arg);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
public function formatPlurar(key:String, num:Int, args:Array<Dynamic>):String {
|
||||
var string:String = getPlural(key, num);
|
||||
for (i in 0...args.length) {
|
||||
var arg:Dynamic = args[i];
|
||||
string = StringTools.replace(string, "{" + i + "}", arg);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
public function getString(key:String):String {
|
||||
return getObject(key);
|
||||
}
|
||||
|
||||
public function getArray(key:String):Array<String> {
|
||||
return getObject(key);
|
||||
}
|
||||
|
||||
|
||||
public function getPlural(key:String, num:Int):String {
|
||||
var array:Array<String> = getArray(key);
|
||||
switch (lang) {
|
||||
case "ru": return plurarRU(array, num);
|
||||
default: return plurarDefault(array, num);
|
||||
}
|
||||
}
|
||||
|
||||
private function getObject(key:String):Dynamic {
|
||||
var path:Array<String> = key.split(".");
|
||||
var tmp:Dynamic = data;
|
||||
for (i in 0...path.length) {
|
||||
if (Reflect.hasField(tmp, path[i])) {
|
||||
tmp = Reflect.field(tmp, path[i]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private function plurarRU(strings:Array<String>, num:Int):String {
|
||||
if (num > 10 && num < 20) return strings[2];
|
||||
var n:Int = num % 10;
|
||||
switch (n) {
|
||||
case 1: return strings[0];
|
||||
case 2: return strings[1];
|
||||
case 3: return strings[1];
|
||||
case 4: return strings[1];
|
||||
default: return strings[2];
|
||||
}
|
||||
}
|
||||
|
||||
private function plurarDefault(strings:Array<String>, num:Int):String {
|
||||
var n:Int = num % 10;
|
||||
switch (n) {
|
||||
case 1: return strings[0];
|
||||
default: return strings[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/main/haxework/log/BaseLogger.hx
Executable file
54
src/main/haxework/log/BaseLogger.hx
Executable file
@@ -0,0 +1,54 @@
|
||||
package haxework.log;
|
||||
|
||||
#if flash
|
||||
import flash.events.ErrorEvent;
|
||||
import flash.errors.Error;
|
||||
#end
|
||||
import haxework.log.ILogger.LogLevel;
|
||||
|
||||
class BaseLogger implements ILogger {
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function log(level:LogLevel, tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
|
||||
write(buildString(level, tag, message, error), p);
|
||||
}
|
||||
|
||||
private function buildString(level:LogLevel, tag:String, message:String, ?error:Dynamic):String {
|
||||
return "[" + level + "] " + tag + " - " + message + (error == null ? "" : " {" + error2strign(error) + "}");
|
||||
}
|
||||
|
||||
public static function error2strign(error:Dynamic):String {
|
||||
#if flash
|
||||
return if (Std.is(error, Error)) {
|
||||
var stack:String = error.getStackTrace();
|
||||
stack == null ? Std.string(error) : stack;
|
||||
} else if (Std.is(error, ErrorEvent)) {
|
||||
var event:ErrorEvent = cast(error, ErrorEvent);
|
||||
event.type + " - " + event.text;
|
||||
} else {
|
||||
Std.string(error);
|
||||
}
|
||||
#else
|
||||
return Std.string(error);
|
||||
#end
|
||||
}
|
||||
|
||||
private function write(text:String, ?p:haxe.PosInfos):Void {}
|
||||
|
||||
public function d(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
|
||||
log(LogLevel.DEBUG, tag, message, error, p);
|
||||
}
|
||||
|
||||
public function i(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
|
||||
log(LogLevel.INFO, tag, message, error, p);
|
||||
}
|
||||
|
||||
public function w(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
|
||||
log(LogLevel.WARNING, tag, message, error, p);
|
||||
}
|
||||
|
||||
public function e(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void {
|
||||
log(LogLevel.ERROR, tag, message, error, p);
|
||||
}
|
||||
}
|
||||
13
src/main/haxework/log/ILogger.hx
Executable file
13
src/main/haxework/log/ILogger.hx
Executable file
@@ -0,0 +1,13 @@
|
||||
package haxework.log;
|
||||
|
||||
enum LogLevel {
|
||||
DEBUG; INFO; WARNING; ERROR;
|
||||
}
|
||||
|
||||
interface ILogger {
|
||||
public function log(level:LogLevel, tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void;
|
||||
public function d(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void;
|
||||
public function i(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void;
|
||||
public function w(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void;
|
||||
public function e(tag:String, message:String, ?error:Dynamic, ?p:haxe.PosInfos):Void;
|
||||
}
|
||||
30
src/main/haxework/log/JSLogger.hx
Executable file
30
src/main/haxework/log/JSLogger.hx
Executable file
@@ -0,0 +1,30 @@
|
||||
package haxework.log;
|
||||
|
||||
import haxework.log.ILogger.LogLevel;
|
||||
import flash.external.ExternalInterface;
|
||||
import haxework.log.BaseLogger;
|
||||
|
||||
class JSLogger extends BaseLogger {
|
||||
|
||||
private var available:Bool;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
available = ExternalInterface.available;
|
||||
}
|
||||
|
||||
override public function e(tag:String, message:String, error:Dynamic = null, p:haxe.PosInfos = null):Void {
|
||||
if (available) {
|
||||
var text:String = buildString(LogLevel.ERROR, tag, message, error);
|
||||
var s:String = p.fileName + ":" + p.lineNumber + ":" + text + "\n";
|
||||
try {ExternalInterface.call("console.error", s);} catch (error:Dynamic) {available = false;}
|
||||
}
|
||||
}
|
||||
|
||||
override private function write(text:String, ?p:haxe.PosInfos):Void {
|
||||
if (available) {
|
||||
var s:String = p.fileName + ":" + p.lineNumber + ":" + text + "\n";
|
||||
try {ExternalInterface.call("console.log", s);} catch (error:Dynamic) {available = false;}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/main/haxework/log/SocketLogger.hx
Executable file
51
src/main/haxework/log/SocketLogger.hx
Executable file
@@ -0,0 +1,51 @@
|
||||
package haxework.log;
|
||||
|
||||
import haxework.log.BaseLogger;
|
||||
|
||||
#if js
|
||||
class SocketLogger extends BaseLogger {}
|
||||
#else
|
||||
|
||||
#if flash
|
||||
import flash.net.Socket;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
#else
|
||||
import sys.net.Host;
|
||||
import sys.net.Socket;
|
||||
#end
|
||||
|
||||
class SocketLogger extends BaseLogger {
|
||||
|
||||
private var socket:Socket;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
socket = new Socket();
|
||||
#if flash
|
||||
socket.addEventListener(IOErrorEvent.IO_ERROR, function(error):Void {
|
||||
//L.e("SocketLogger", "", error);
|
||||
});
|
||||
socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(error):Void {
|
||||
//L.e("SocketLogger", "", error);
|
||||
});
|
||||
socket.connect(CompilationOption.get("debug.address"), Std.parseInt(CompilationOption.get("debug.port")));
|
||||
#else
|
||||
socket.connect(new Host(CompilationOption.get("debug.address")), Std.parseInt(CompilationOption.get("debug.port")));
|
||||
#end
|
||||
}
|
||||
|
||||
override private function write(text:String, ?p:haxe.PosInfos):Void {
|
||||
try {
|
||||
var s:String = p.fileName + ":" + p.lineNumber + ":" + text + "\n";
|
||||
#if flash
|
||||
socket.writeUTF(s);
|
||||
socket.flush();
|
||||
#else
|
||||
socket.write(s);
|
||||
#end
|
||||
} catch (error:Dynamic) {}
|
||||
}
|
||||
}
|
||||
|
||||
#end
|
||||
14
src/main/haxework/log/TraceLogger.hx
Executable file
14
src/main/haxework/log/TraceLogger.hx
Executable file
@@ -0,0 +1,14 @@
|
||||
package haxework.log;
|
||||
|
||||
import haxework.log.ILogger.LogLevel;
|
||||
|
||||
class TraceLogger extends BaseLogger {
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
}
|
||||
|
||||
override private function write(text:String, ?p:haxe.PosInfos):Void {
|
||||
haxe.Log.trace(text, p);
|
||||
}
|
||||
}
|
||||
183
src/main/haxework/net/BaseLoader.hx
Executable file
183
src/main/haxework/net/BaseLoader.hx
Executable file
@@ -0,0 +1,183 @@
|
||||
package haxework.net;
|
||||
|
||||
import haxe.Timer;
|
||||
import flash.net.URLRequestMethod;
|
||||
import flash.events.ProgressEvent;
|
||||
import haxework.net.manage.ILoaderManager;
|
||||
import haxework.provider.Provider;
|
||||
import flash.utils.ByteArray;
|
||||
import haxework.net.callback.Callback;
|
||||
import haxework.net.callback.ICallback;
|
||||
import flash.events.Event;
|
||||
|
||||
class BaseLoader<T> implements ILoader<T> {
|
||||
|
||||
private static inline var TAG:String = "Loader";
|
||||
|
||||
//ToDo: move to LoaderManager
|
||||
public static var urlProcessors(default, null):Array<String->String> = new Array<String->String>();
|
||||
public static function prepareUrl(url:String):String { for (p in urlProcessors) url = p(url); return url; }
|
||||
|
||||
public static var proxy(default, default):String->String;
|
||||
|
||||
|
||||
public var timeout(default, default):Int;
|
||||
public var busy(default, null):Bool;
|
||||
public var completed(default, null):Float;
|
||||
|
||||
private var url:String;
|
||||
private var method:String;
|
||||
private var data:Null<Dynamic>;
|
||||
private var callback:ICallback<T>;
|
||||
private var timer:Timer;
|
||||
|
||||
public function new(?timeout = 0) {
|
||||
this.timeout = timeout;
|
||||
busy = false;
|
||||
completed = Math.NaN;
|
||||
}
|
||||
|
||||
public function request(url:String, method:String, data:Dynamic = null):ICallback<T> {
|
||||
if (busy) throw "Busy";
|
||||
busy = true;
|
||||
this.url = url;
|
||||
this.method = method;
|
||||
this.data = data;
|
||||
callback = new Callback<T>();
|
||||
var url:String = this.url;
|
||||
//L.d(TAG, "Request: " + prepareUrl(url));
|
||||
//internalRequest(prepareUrl(url));
|
||||
Provider.get(ILoaderManager).add(this);
|
||||
return callback;
|
||||
}
|
||||
|
||||
private function cockTimeout():Void {
|
||||
if (timeout > 0) {
|
||||
timer = new Timer(timeout);
|
||||
timer.run = callTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
private function cancelTimeout():Void {
|
||||
if (timer != null) {
|
||||
timer.stop();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function fromBytes(data:ByteArray):ICallback<T> {
|
||||
if (busy) throw "Busy";
|
||||
busy = true;
|
||||
callback = new Callback<T>();
|
||||
internalFromBytes(data);
|
||||
return callback;
|
||||
}
|
||||
|
||||
public function GET(url:String, data:Dynamic = null):ICallback<T> {
|
||||
#if openfl
|
||||
if (StringTools.startsWith(url, "%assets%")) {
|
||||
var path:String = url.substring(9);
|
||||
var bytes:ByteArray = openfl.Assets.getBytes(path);
|
||||
if (bytes == null) {
|
||||
Timer.delay(function() {
|
||||
var c:ICallback<T> = callback;
|
||||
dispose();
|
||||
c.callFail("Bytes is null for asset: " + path);
|
||||
}, 1);
|
||||
} else {
|
||||
return fromBytes(bytes);
|
||||
}
|
||||
}
|
||||
#end
|
||||
return request(url, URLRequestMethod.GET, data);
|
||||
}
|
||||
|
||||
public function POST(url:String, data:Dynamic = null):ICallback<T> {
|
||||
return request(url, URLRequestMethod.POST, data);
|
||||
}
|
||||
|
||||
public function DELETE(url:String, data:Dynamic = null):ICallback<T> {
|
||||
return request(url, URLRequestMethod.DELETE, data);
|
||||
}
|
||||
|
||||
private function internalRequest(url:String):Void {
|
||||
throw "Abstract";
|
||||
}
|
||||
|
||||
private function internalFromBytes(data:ByteArray):Void {
|
||||
throw "Abstract";
|
||||
}
|
||||
|
||||
private function onInit(e:Event):Void {}
|
||||
|
||||
private function onProgress(e:ProgressEvent):Void {
|
||||
completed = e.bytesLoaded / e.bytesTotal;
|
||||
}
|
||||
|
||||
private function onComplete(e:Event):Void {
|
||||
var data:T = extrudeResult(e);
|
||||
if (data != null) {
|
||||
var c:ICallback<T> = callback;
|
||||
dispose();
|
||||
c.callSuccess(data);
|
||||
}
|
||||
}
|
||||
|
||||
private function onSecurityError(e:Event):Void {
|
||||
if (proxy == null) {
|
||||
onError(e);
|
||||
} else {
|
||||
cancelTimeout();
|
||||
internalRequest(proxy(buildUrl()));
|
||||
}
|
||||
}
|
||||
|
||||
private function onError(e:Event):Void {
|
||||
var c:ICallback<T> = callback;
|
||||
dispose();
|
||||
c.callFail(e);
|
||||
}
|
||||
|
||||
private function callTimeout():Void {
|
||||
var c:ICallback<T> = callback;
|
||||
var error:String = "Timeout for: " + url;
|
||||
dispose();
|
||||
c.callFail(error);
|
||||
}
|
||||
|
||||
private function extrudeResult(e:Event):T {
|
||||
throw "Abstract";
|
||||
return null;
|
||||
}
|
||||
|
||||
private function dispose():Void {
|
||||
cancelTimeout();
|
||||
url = null;
|
||||
data = null;
|
||||
callback = null;
|
||||
busy = false;
|
||||
completed = Math.NaN;
|
||||
Provider.get(ILoaderManager).release(this);
|
||||
}
|
||||
|
||||
public function cancel():Void {
|
||||
dispose();
|
||||
}
|
||||
|
||||
private function buildUrl():String {
|
||||
var u:String = url;
|
||||
if (data != null && method == URLRequestMethod.GET) {
|
||||
var a:Array<String> = [];
|
||||
for (key in Reflect.fields(data)) {
|
||||
a.push(key + "=" + Reflect.field(data, key));
|
||||
}
|
||||
u += "?" + a.join("&");
|
||||
}
|
||||
return prepareUrl(u);
|
||||
}
|
||||
|
||||
public function run():Void {
|
||||
internalRequest(buildUrl());
|
||||
}
|
||||
}
|
||||
|
||||
99
src/main/haxework/net/BaseMediaLoader.hx
Executable file
99
src/main/haxework/net/BaseMediaLoader.hx
Executable file
@@ -0,0 +1,99 @@
|
||||
package haxework.net;
|
||||
|
||||
import haxework.net.callback.Callback;
|
||||
import haxework.net.callback.ICallback;
|
||||
import haxework.provider.Provider;
|
||||
import haxework.storage.IStorage;
|
||||
import flash.display.BitmapData;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.system.Security;
|
||||
import flash.system.SecurityDomain;
|
||||
import flash.system.ApplicationDomain;
|
||||
import flash.system.LoaderContext;
|
||||
import flash.utils.ByteArray;
|
||||
import haxework.net.BaseLoader;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.Event;
|
||||
import flash.net.URLRequest;
|
||||
import flash.display.Loader;
|
||||
|
||||
class BaseMediaLoader<T> extends BaseLoader<T> {
|
||||
|
||||
private var loader:Loader;
|
||||
|
||||
//ToDo: if storage use and local domen
|
||||
//exclude youtube player
|
||||
/*override public function GET(url:String, data:Dynamic = null):ICallback<T> {
|
||||
var storage:IStorage = Provider.get(IStorage);
|
||||
return if (storage.exists(url)) {
|
||||
fromBytes(storage.read(url));
|
||||
} else {
|
||||
var callback:ICallback<T> = new Callback<T>();
|
||||
new BytesLoader().GET(url)
|
||||
.success(function(data:ByteArray):Void {
|
||||
storage.write(url, data);
|
||||
fromBytes(data).glue(callback);
|
||||
})
|
||||
.fail(callback.callFail);
|
||||
callback;
|
||||
}
|
||||
}*/
|
||||
|
||||
override private function internalRequest(url:String):Void {
|
||||
cockTimeout();
|
||||
loader = buildLoader();
|
||||
loader.load(new URLRequest(url), buildLoaderContext(false));
|
||||
}
|
||||
|
||||
override private function internalFromBytes(data:ByteArray):Void {
|
||||
loader = buildLoader();
|
||||
#if flash
|
||||
loader.loadBytes(data, buildLoaderContext(true));
|
||||
#else
|
||||
loader.loadBytes(data);
|
||||
#end
|
||||
}
|
||||
|
||||
private function buildLoaderContext(bytes:Bool):LoaderContext {
|
||||
#if flash
|
||||
return switch (Security.sandboxType) {
|
||||
case Security.REMOTE:
|
||||
//null;
|
||||
bytes ? null : new LoaderContext(true, ApplicationDomain.currentDomain, SecurityDomain.currentDomain);
|
||||
case Security.APPLICATION:
|
||||
var loaderContext:LoaderContext = new LoaderContext();
|
||||
loaderContext.allowLoadBytesCodeExecution = true;
|
||||
loaderContext;
|
||||
default:
|
||||
null;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
private function buildLoader():Loader {
|
||||
var loader:Loader = new Loader();
|
||||
loader.contentLoaderInfo.addEventListener(Event.INIT, onInit);
|
||||
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onError);
|
||||
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
|
||||
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
|
||||
return loader;
|
||||
}
|
||||
|
||||
override private function dispose():Void {
|
||||
super.dispose();
|
||||
if (loader != null) {
|
||||
loader.contentLoaderInfo.removeEventListener(Event.INIT, onInit);
|
||||
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete);
|
||||
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onError);
|
||||
loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
|
||||
loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress);
|
||||
#if flash try { loader.close(); } catch (error:Dynamic) {} #end
|
||||
loader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
112
src/main/haxework/net/BaseURLLoader.hx
Executable file
112
src/main/haxework/net/BaseURLLoader.hx
Executable file
@@ -0,0 +1,112 @@
|
||||
package haxework.net;
|
||||
|
||||
import haxe.Timer;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.net.URLRequestHeader;
|
||||
import flash.net.URLRequestMethod;
|
||||
import haxework.net.callback.ICallback;
|
||||
import flash.net.URLVariables;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.net.URLLoaderDataFormat;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.net.URLRequest;
|
||||
import flash.events.Event;
|
||||
import flash.net.URLLoader;
|
||||
|
||||
class BaseURLLoader<T> extends BaseLoader<T> {
|
||||
|
||||
private var dataFormat:URLLoaderDataFormat;
|
||||
private var loader:URLLoader;
|
||||
|
||||
public function new(dateFormat:URLLoaderDataFormat = null) {
|
||||
super();
|
||||
this.dataFormat = (dateFormat == null ? URLLoaderDataFormat.TEXT : dateFormat);
|
||||
}
|
||||
|
||||
override private function internalRequest(url:String):Void {
|
||||
cockTimeout();
|
||||
loader = buildLoader();
|
||||
var request:URLRequest = new URLRequest(url);
|
||||
if (method != URLRequestMethod.POST && method != URLRequestMethod.GET) {
|
||||
request.method = URLRequestMethod.POST;
|
||||
request.requestHeaders.push(new URLRequestHeader("X-HTTP-Method-Override", method));
|
||||
} else {
|
||||
request.method = method;
|
||||
}
|
||||
if (data != null && method == URLRequestMethod.POST) {
|
||||
var variables:URLVariables = new URLVariables();
|
||||
for (key in Reflect.fields(data)) {
|
||||
Reflect.setField(variables, key, Reflect.field(data, key));
|
||||
}
|
||||
request.data = variables;
|
||||
}
|
||||
loader.load(request);
|
||||
}
|
||||
|
||||
override private function internalFromBytes(data:ByteArray):Void {
|
||||
var c:ICallback<T> = callback;
|
||||
if (data == null) {
|
||||
c.callFailAsync("Content not found");
|
||||
} else {
|
||||
var data:T = extrudeResultFromBytes(data);
|
||||
c.callSuccessAsync(data);
|
||||
}
|
||||
Timer.delay(dispose, 1);
|
||||
}
|
||||
|
||||
private function extrudeResultFromBytes(bytes:ByteArray):T {
|
||||
throw "Abstract";
|
||||
return null;
|
||||
}
|
||||
|
||||
private function buildLoader():URLLoader {
|
||||
var loader:URLLoader = new URLLoader();
|
||||
loader.dataFormat = dataFormat;
|
||||
loader.addEventListener(Event.COMPLETE, onComplete);
|
||||
loader.addEventListener(IOErrorEvent.IO_ERROR, onError);
|
||||
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
|
||||
loader.addEventListener(ProgressEvent.PROGRESS, onProgress);
|
||||
return loader;
|
||||
}
|
||||
|
||||
override private function dispose():Void {
|
||||
super.dispose();
|
||||
if (loader != null) {
|
||||
loader.removeEventListener(Event.COMPLETE, onComplete);
|
||||
loader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
|
||||
loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
|
||||
loader.removeEventListener(ProgressEvent.PROGRESS, onProgress);
|
||||
try { loader.close(); } catch (error:Dynamic) {}
|
||||
loader = null;
|
||||
}
|
||||
}
|
||||
|
||||
override public function cancel():Void {
|
||||
if (loader != null) {
|
||||
try {loader.close();} catch (error:Dynamic) {}
|
||||
}
|
||||
super.cancel();
|
||||
}
|
||||
|
||||
override private function onError(e:Event):Void {
|
||||
var c:ICallback<T> = callback;
|
||||
var error:String = extrudeError(loader.data);
|
||||
if (error != null) error = url + ": " + error;
|
||||
dispose();
|
||||
c.callFail(error != null ? error : e);
|
||||
}
|
||||
|
||||
private function extrudeError(data:Dynamic):String {
|
||||
return if (data == null) null else {
|
||||
var s:String = Std.string(data);
|
||||
var r:EReg = ~/<h1>(.*?)<\/h1>/;
|
||||
if (r.match(s)) {
|
||||
r.matched(1);
|
||||
} else {
|
||||
s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
51
src/main/haxework/net/BatchLoader.hx
Executable file
51
src/main/haxework/net/BatchLoader.hx
Executable file
@@ -0,0 +1,51 @@
|
||||
package haxework.net;
|
||||
|
||||
import haxework.net.callback.Callback;
|
||||
import haxework.net.callback.ICallback;
|
||||
import flash.events.Event;
|
||||
import flash.net.URLLoader;
|
||||
|
||||
class BatchLoader<T> {
|
||||
|
||||
public var factory:Class<ILoader<T>>;
|
||||
|
||||
public function new(factory:Class<ILoader<T>>) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public function GET(urls:Array<String>):ICallback<Array<T>> {
|
||||
var callbacks:Array<ICallback<T>> = urls.map(function(url:String):ICallback<T> {
|
||||
var loader:ILoader<T> = Type.createInstance(factory, []);
|
||||
return loader.GET(url);
|
||||
});
|
||||
return new BatchCallback(callbacks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BatchCallback<T> extends Callback<Array<T>> {
|
||||
|
||||
private var data:Array<T>;
|
||||
private var counter:Int;
|
||||
|
||||
public function new(callbacks:Array<ICallback<T>>) {
|
||||
super();
|
||||
data = new Array<T>();
|
||||
counter = callbacks.length;
|
||||
for (i in 0...callbacks.length) {
|
||||
register(callbacks[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
private function register(callback:ICallback<T>, index:Int):Void {
|
||||
callback
|
||||
.success(function(d:T):Void {
|
||||
data[index] = d;
|
||||
if (--counter == 0) callSuccess(data);
|
||||
})
|
||||
.fail(function(error:Dynamic):Void {
|
||||
L.e("BatchLoader", "", error);
|
||||
if (--counter == 0) callSuccess(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
21
src/main/haxework/net/BytesLoader.hx
Executable file
21
src/main/haxework/net/BytesLoader.hx
Executable file
@@ -0,0 +1,21 @@
|
||||
package haxework.net;
|
||||
|
||||
import flash.net.URLLoaderDataFormat;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.net.URLLoader;
|
||||
import flash.events.Event;
|
||||
|
||||
class BytesLoader extends BaseURLLoader<ByteArray> {
|
||||
|
||||
public function new() {
|
||||
super(URLLoaderDataFormat.BINARY);
|
||||
}
|
||||
|
||||
override private function extrudeResult(e:Event):ByteArray {
|
||||
return cast(cast(e.currentTarget, URLLoader).data, ByteArray);
|
||||
}
|
||||
|
||||
override private function extrudeResultFromBytes(bytes:ByteArray):ByteArray {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
59
src/main/haxework/net/ExternalImageLoader.hx
Executable file
59
src/main/haxework/net/ExternalImageLoader.hx
Executable file
@@ -0,0 +1,59 @@
|
||||
package haxework.net;
|
||||
|
||||
import haxework.net.callback.ICallback;
|
||||
import flash.system.Security;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.display.LoaderInfo;
|
||||
import flash.display.Loader;
|
||||
import flash.display.Bitmap;
|
||||
import flash.events.Event;
|
||||
import flash.display.BitmapData;
|
||||
|
||||
class ExternalImageLoader extends BaseMediaLoader<BitmapData> {
|
||||
|
||||
private var internalLoader:InternalLoader;
|
||||
|
||||
override private function extrudeResult(e:Event):BitmapData {
|
||||
var loader:Loader = cast(e.currentTarget, LoaderInfo).loader;
|
||||
#if flash
|
||||
if (Security.sandboxType == Security.APPLICATION) {
|
||||
var content:Bitmap = cast(loader.content,Bitmap);
|
||||
return content.bitmapData;
|
||||
} else {
|
||||
#end
|
||||
internalLoader = new InternalLoader();
|
||||
internalLoader.fromBytes(loader.contentLoaderInfo.bytes)
|
||||
.success(function(data:BitmapData):Void {
|
||||
var c:ICallback<BitmapData> = callback;
|
||||
dispose();
|
||||
c.callSuccess(data);
|
||||
})
|
||||
.fail(function(error:Dynamic):Void {
|
||||
var c:ICallback<BitmapData> = callback;
|
||||
dispose();
|
||||
c.callFail(e);
|
||||
});
|
||||
return null;
|
||||
#if flash} #end
|
||||
}
|
||||
|
||||
override public function dispose():Void {
|
||||
super.dispose();
|
||||
if (internalLoader != null) {
|
||||
internalLoader.dispose();
|
||||
internalLoader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InternalLoader extends BaseMediaLoader<BitmapData> {
|
||||
|
||||
override private function extrudeResult(e:Event):BitmapData {
|
||||
var loader:Loader = cast(e.currentTarget, LoaderInfo).loader;
|
||||
var bitmapData:BitmapData = new BitmapData(Math.round(loader.width), Math.round(loader.height), true, 0x00000000);
|
||||
bitmapData.draw(loader);
|
||||
loader.unloadAndStop();
|
||||
return bitmapData;
|
||||
}
|
||||
}
|
||||
|
||||
19
src/main/haxework/net/ILoader.hx
Executable file
19
src/main/haxework/net/ILoader.hx
Executable file
@@ -0,0 +1,19 @@
|
||||
package haxework.net;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import haxework.net.callback.ICallback;
|
||||
|
||||
interface ILoader<T> {
|
||||
public var timeout(default, default):Int;
|
||||
public var busy(default, null):Bool;
|
||||
public var completed(default, null):Float;
|
||||
|
||||
public function request(url:String, method:String, data:Dynamic = null):ICallback<T>;
|
||||
public function fromBytes(data:ByteArray):ICallback<T>;
|
||||
public function GET(url:String, data:Dynamic = null):ICallback<T>;
|
||||
public function POST(url:String, data:Dynamic = null):ICallback<T>;
|
||||
public function DELETE(url:String, data:Dynamic = null):ICallback<T>;
|
||||
public function cancel():Void;
|
||||
|
||||
public function run():Void;
|
||||
}
|
||||
17
src/main/haxework/net/ImageLoader.hx
Executable file
17
src/main/haxework/net/ImageLoader.hx
Executable file
@@ -0,0 +1,17 @@
|
||||
package haxework.net;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import flash.display.LoaderInfo;
|
||||
import flash.display.Loader;
|
||||
import flash.display.Bitmap;
|
||||
import flash.events.Event;
|
||||
import flash.display.BitmapData;
|
||||
|
||||
class ImageLoader extends BaseMediaLoader<BitmapData> {
|
||||
|
||||
override private function extrudeResult(e:Event):BitmapData {
|
||||
var content:Bitmap = cast(cast(e.currentTarget, LoaderInfo).loader.content,Bitmap);
|
||||
return content.bitmapData;
|
||||
}
|
||||
}
|
||||
|
||||
34
src/main/haxework/net/JsonLoader.hx
Executable file
34
src/main/haxework/net/JsonLoader.hx
Executable file
@@ -0,0 +1,34 @@
|
||||
package haxework.net;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import flash.errors.Error;
|
||||
import flash.events.Event;
|
||||
import flash.net.URLLoader;
|
||||
import haxe.Json;
|
||||
|
||||
class JsonLoader<T> extends BaseURLLoader<T> {
|
||||
|
||||
override private function extrudeResult(e:Event):T {
|
||||
var str:String = null;
|
||||
var data:T = null;
|
||||
try {
|
||||
str = Std.string(cast(e.currentTarget, URLLoader).data);
|
||||
data = Json.parse(str);
|
||||
} catch (error:Dynamic) {
|
||||
throw new Error(error + ": " + url);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
override private function extrudeResultFromBytes(bytes:ByteArray):T {
|
||||
var str:String = null;
|
||||
var data:T = null;
|
||||
try {
|
||||
str = bytes.readUTFBytes(bytes.length);
|
||||
data = Json.parse(str);
|
||||
} catch (error:Dynamic) {
|
||||
throw new Error(error + ": " + url);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
16
src/main/haxework/net/SwfLoader.hx
Executable file
16
src/main/haxework/net/SwfLoader.hx
Executable file
@@ -0,0 +1,16 @@
|
||||
package haxework.net;
|
||||
|
||||
import flash.display.MovieClip;
|
||||
import flash.display.LoaderInfo;
|
||||
import flash.display.Loader;
|
||||
import flash.events.Event;
|
||||
|
||||
class SwfLoader extends BaseMediaLoader<MovieClip> {
|
||||
|
||||
override private function extrudeResult(e:Event):MovieClip {
|
||||
var content:MovieClip = cast(cast(e.currentTarget, LoaderInfo).loader.content, MovieClip);
|
||||
content.gotoAndStop(0);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
11
src/main/haxework/net/TextLoader.hx
Executable file
11
src/main/haxework/net/TextLoader.hx
Executable file
@@ -0,0 +1,11 @@
|
||||
package haxework.net;
|
||||
|
||||
import flash.net.URLLoader;
|
||||
import flash.events.Event;
|
||||
|
||||
class TextLoader extends BaseURLLoader<String> {
|
||||
|
||||
override private function extrudeResult(e:Event):String {
|
||||
return Std.string(cast(e.currentTarget, URLLoader).data);
|
||||
}
|
||||
}
|
||||
11
src/main/haxework/net/XmlLoader.hx
Executable file
11
src/main/haxework/net/XmlLoader.hx
Executable file
@@ -0,0 +1,11 @@
|
||||
package haxework.net;
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.net.URLLoader;
|
||||
|
||||
class XmlLoader extends BaseURLLoader<Xml> {
|
||||
|
||||
override private function extrudeResult(e:Event):Xml {
|
||||
return Xml.parse(Std.string(cast(e.currentTarget, URLLoader).data));
|
||||
}
|
||||
}
|
||||
16
src/main/haxework/net/callback/AutoCallback.hx
Executable file
16
src/main/haxework/net/callback/AutoCallback.hx
Executable file
@@ -0,0 +1,16 @@
|
||||
package haxework.net.callback;
|
||||
|
||||
class AutoCallback {
|
||||
|
||||
public static function success<T>(?value:T):ICallback<T> {
|
||||
var callback:ICallback<T> = new Callback<T>();
|
||||
callback.callSuccessAsync(value);
|
||||
return callback;
|
||||
}
|
||||
|
||||
public static function fail<T>(error:Dynamic):ICallback<T> {
|
||||
var callback:ICallback<T> = new Callback<T>();
|
||||
callback.callFailAsync(error);
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
73
src/main/haxework/net/callback/Callback.hx
Executable file
73
src/main/haxework/net/callback/Callback.hx
Executable file
@@ -0,0 +1,73 @@
|
||||
package haxework.net.callback;
|
||||
|
||||
import haxe.Timer;
|
||||
|
||||
class Callback<T> implements ICallback<T> {
|
||||
|
||||
public static function build<T>():ICallback<T> {
|
||||
return new Callback<T>();
|
||||
}
|
||||
|
||||
private var _success:Null<T -> Void>;
|
||||
private var _fail:Null<Dynamic -> Void>;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function success(f:T -> Void):ICallback<T> {
|
||||
_success = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public function fail(f:Dynamic -> Void):ICallback<T> {
|
||||
_fail = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public function any(f:Dynamic -> Void):ICallback<T> {
|
||||
_success = f;
|
||||
_fail = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public function callSuccess(data:T):Void {
|
||||
try {
|
||||
if (_success != null) _success(data);
|
||||
} catch (error:Dynamic) {
|
||||
callFail(error);
|
||||
}
|
||||
dispose();
|
||||
}
|
||||
|
||||
public function callSuccessAsync(data:T):Void {
|
||||
Timer.delay(function():Void {
|
||||
callSuccess(data);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
public function callFail(error:Dynamic):Void {
|
||||
try {
|
||||
if (_fail != null) _fail(error);
|
||||
} catch (error:Dynamic) {
|
||||
L.d("Callback", "", error);
|
||||
}
|
||||
dispose();
|
||||
}
|
||||
|
||||
public function callFailAsync(error:Dynamic):Void {
|
||||
Timer.delay(function():Void {
|
||||
callFail(error);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
public function glue(callback:ICallback<T>):ICallback<T> {
|
||||
this._success = callback.callSuccess;
|
||||
this._fail = callback.callFail;
|
||||
//callback.dispose(); //ToDo:
|
||||
return this;
|
||||
}
|
||||
|
||||
public function dispose():Void {
|
||||
_success = null;
|
||||
_fail = null;
|
||||
}
|
||||
}
|
||||
15
src/main/haxework/net/callback/ICallback.hx
Executable file
15
src/main/haxework/net/callback/ICallback.hx
Executable file
@@ -0,0 +1,15 @@
|
||||
package haxework.net.callback;
|
||||
|
||||
import haxework.core.IDisposable;
|
||||
|
||||
interface ICallback<T> extends IDisposable {
|
||||
public function success(f:T -> Void):ICallback<T>;
|
||||
public function fail(f:Dynamic -> Void):ICallback<T>;
|
||||
public function any(f:Dynamic -> Void):ICallback<T>;
|
||||
public function callSuccess(data:T):Void;
|
||||
public function callSuccessAsync(data:T):Void;
|
||||
public function callFail(error:Dynamic):Void;
|
||||
public function callFailAsync(error:Dynamic):Void;
|
||||
public function glue(callback:ICallback<T>):ICallback<T>;
|
||||
}
|
||||
|
||||
10
src/main/haxework/net/manage/ILoaderManager.hx
Executable file
10
src/main/haxework/net/manage/ILoaderManager.hx
Executable file
@@ -0,0 +1,10 @@
|
||||
package haxework.net.manage;
|
||||
|
||||
interface ILoaderManager {
|
||||
public var actives(default, null):Array<ILoader<Dynamic>>;
|
||||
public var queue(default, null):Array<ILoader<Dynamic>>;
|
||||
public var limit(default, default):Int;
|
||||
|
||||
public function add(loader:ILoader<Dynamic>):Void;
|
||||
public function release(loader:ILoader<Dynamic>):Void;
|
||||
}
|
||||
36
src/main/haxework/net/manage/LoaderManager.hx
Executable file
36
src/main/haxework/net/manage/LoaderManager.hx
Executable file
@@ -0,0 +1,36 @@
|
||||
package haxework.net.manage;
|
||||
|
||||
class LoaderManager implements ILoaderManager {
|
||||
|
||||
private static inline var TAG:String = "LoaderManager";
|
||||
|
||||
public var actives(default, null):Array<ILoader<Dynamic>>;
|
||||
public var queue(default, null):Array<ILoader<Dynamic>>;
|
||||
public var limit(default, default):Int;
|
||||
|
||||
public function new() {
|
||||
queue = new Array<ILoader<Dynamic>>();
|
||||
actives = new Array<ILoader<Dynamic>>();
|
||||
limit = 10;
|
||||
}
|
||||
|
||||
public function add(loader:ILoader<Dynamic>):Void {
|
||||
if (actives.length >= limit) {
|
||||
queue.push(loader);
|
||||
} else {
|
||||
run(loader);
|
||||
}
|
||||
}
|
||||
|
||||
private function run(loader:ILoader<Dynamic>):Void {
|
||||
actives.push(loader);
|
||||
loader.run();
|
||||
}
|
||||
|
||||
public function release(loader:ILoader<Dynamic>):Void {
|
||||
actives.remove(loader);
|
||||
if (queue.length > 0 && actives.length < limit) {
|
||||
run(queue.shift());
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/main/haxework/net/order/IOrderSupplier.hx
Executable file
17
src/main/haxework/net/order/IOrderSupplier.hx
Executable file
@@ -0,0 +1,17 @@
|
||||
package haxework.net.order;
|
||||
|
||||
import haxework.net.callback.ICallback;
|
||||
|
||||
typedef Order<T> = {
|
||||
var id:String;
|
||||
var data:Null<T>;
|
||||
var callbacks:Array<ICallback<T>>;
|
||||
var clients:Int;
|
||||
}
|
||||
|
||||
interface IOrderSupplier {
|
||||
public var orders(default, null):Map<String, Order<Dynamic>>;
|
||||
|
||||
public function request<T>(url:String, clazz:Class<T>):ICallback<T>;
|
||||
public function release(url:String, ?force:Bool = false):Void;
|
||||
}
|
||||
92
src/main/haxework/net/order/OrderSupplier.hx
Executable file
92
src/main/haxework/net/order/OrderSupplier.hx
Executable file
@@ -0,0 +1,92 @@
|
||||
package haxework.net.order;
|
||||
|
||||
import haxework.net.callback.AutoCallback;
|
||||
import haxework.net.order.IOrderSupplier.Order;
|
||||
import haxework.storage.IStorage;
|
||||
import haxework.provider.Provider;
|
||||
import haxework.net.callback.Callback;
|
||||
import flash.display.BitmapData;
|
||||
import haxework.net.callback.ICallback;
|
||||
|
||||
class OrderSupplier implements IOrderSupplier {
|
||||
|
||||
private static inline var TAG:String = "OrderSupplier";
|
||||
|
||||
public var orders(default, null):Map<String, Order<Dynamic>>;
|
||||
|
||||
public function new() {
|
||||
orders = new Map<String, Order<Dynamic>>();
|
||||
}
|
||||
|
||||
public function request<T>(url:String, clazz:Class<T>):ICallback<T> {
|
||||
if (orders.exists(url)) {
|
||||
var order:Order<T> = orders.get(url);
|
||||
order.clients++;
|
||||
//L.d(TAG, "Request(" + order.clients + "): " + url);
|
||||
if (order.data == null) {
|
||||
var callback:ICallback<T> = new Callback<T>();
|
||||
order.callbacks.push(callback);
|
||||
return callback;
|
||||
} else {
|
||||
return AutoCallback.success(order.data);
|
||||
}
|
||||
} else {
|
||||
var callback:ICallback<T> = new Callback<T>();
|
||||
var order:Order<T> = {
|
||||
id:url,
|
||||
data:null,
|
||||
callbacks:[callback],
|
||||
clients:1
|
||||
}
|
||||
//L.d(TAG, "Request(" + order.clients + "): " + url);
|
||||
orders.set(url, order);
|
||||
var loader:ILoader<T> = buildLoader(clazz);
|
||||
loader.GET(url)
|
||||
.success(function(data:T):Void {
|
||||
if (orders.exists(url)) {
|
||||
var order:Order<T> = orders.get(url);
|
||||
order.data = data;
|
||||
for (callback in order.callbacks) callback.callSuccess(data);
|
||||
order.callbacks = [];
|
||||
}
|
||||
})
|
||||
.fail(function(error:Dynamic):Void {
|
||||
if (orders.exists(url)) for (callback in orders.get(url).callbacks) callback.callFail(error);
|
||||
orders.remove(url);
|
||||
});
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
|
||||
public function release(url:String, ?force:Bool = false):Void {
|
||||
if (orders.exists(url)) {
|
||||
var order:Order<Dynamic> = orders.get(url);
|
||||
if (--order.clients <= 0 || force) {
|
||||
var data:Dynamic = order.data;
|
||||
if (Std.is(data, BitmapData)) {
|
||||
cast(data, BitmapData).dispose();
|
||||
}
|
||||
orders.remove(url);
|
||||
}
|
||||
//L.d(TAG, "Release(" + order.clients + "): " + url);
|
||||
//log();
|
||||
}
|
||||
}
|
||||
|
||||
private function buildLoader<T>(clazz:Class<T>):ILoader<T> {
|
||||
var c:Class<Dynamic> = clazz;
|
||||
return if (c == BitmapData) {
|
||||
var loader:ILoader<T> = untyped new ImageLoader();
|
||||
loader.timeout = 7000; //ToDo: hardcode timeout for loading images
|
||||
loader;
|
||||
} else {
|
||||
throw "Unsupported order: " + c;
|
||||
}
|
||||
}
|
||||
|
||||
private function log():Void {
|
||||
L.d(TAG, "\n" + Lambda.map(orders, function(order:Order<Dynamic>):String {
|
||||
return "(" + order.clients + ") " + order.id;
|
||||
}).join("\n"));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user