[gui] update

This commit is contained in:
2019-03-06 17:07:26 +03:00
parent c2ab82b351
commit b0824773c9
46 changed files with 583 additions and 737 deletions

View File

@@ -1,38 +1,50 @@
package demo;
import haxework.gui.frame.IFrameSwitcher;
import haxework.net.JsonLoader;
import demo.popup.ColorPopup;
import haxework.App;
import haxework.gui.frame.FrameSwitcher;
import haxework.gui.IGroupView;
import haxework.gui.IView;
import haxework.gui.Root;
import haxework.gui.ToggleButtonView;
import haxework.gui.VGroupView;
import haxework.log.TraceLogger;
import haxework.net.manage.ILoaderManager;
import haxework.net.manage.LoaderManager;
import haxework.resources.IResources;
import haxework.resources.Resources;
@:template class Demo extends VGroupView {
@:provide static var resources:IResources;
@:provide static var manager:ILoaderManager;
public static function main() {
L.push(new TraceLogger());
resources = new Resources();
manager = new LoaderManager();
resources.image.put("logo", HaxeLogo.resolve());
Theme.setColor(0x33aa33);
var demo = new Demo();
demo.switcher.change("list_form");
Root.bind(demo);
Root.instance.onResize.connect(function(rect) trace("resize", rect));
}
@:view var switcher:IFrameSwitcher;
@:template class DemoView extends VGroupView {
@:view var switcher:FrameSwitcher;
@:view var tabs:IGroupView;
private function init():Void {
switcher.change("list_form");
}
private function onFrameSwitch(frame:IView<Dynamic>):Void {
for (view in tabs.views) cast(view, ToggleButtonView).on = view.id == frame.id;
}
private function choiceColor():Void {
new ColorPopup()
.show()
.then(function(color) Theme.setColor(color))
.catchError(function(e) {});
}
}
class Demo extends App {
public static function main() {
L.push(new TraceLogger());
var app = new App();
app.resources.image.put("logo", HaxeLogo.resolve());
Theme.setColor(0x33aa33);
app.start(new DemoView());
new JsonLoader().GET("http://umix.tv/channel/data2/renova.json")
.then(function(data:Array<Model>) {
app.resources.any.put("data", data);
app.resources.any.put("data50", Util.marray(data, 50));
})
.catchError(function(error) trace(error));
}
}

View File

@@ -33,19 +33,23 @@ views:
+onPress: "$code:switcher.change('data_form')"
- id: switcher
$type: haxework.gui.frame.FrameSwitcher
skin: $r:skin:border
skin: $r:skin:panel
animateFactory: { $class: haxework.animate.SlideAnimate }
+onSwitch: $this:onFrameSwitch
geometry.size.stretch: true
geometry.padding: 5
views:
- id: list_form
$type: demo.form.ListForm
skin: $r:skin:background
geometry.size.stretch: true
- id: tail_form
$type: demo.form.TailForm
skin: $r:skin:background
geometry.size.stretch: true
- id: data_form
$type: demo.form.DataForm
skin: $r:skin:background
geometry.size.stretch: true
- $type: haxework.gui.HGroupView
geometry.size.percent.width: 100
@@ -55,23 +59,8 @@ views:
- $type: haxework.gui.ButtonView
geometry.padding: [25, 8]
skin: $r:skin:button
text: green
+onPress: "$code:Theme.setColor(0x33AA33)"
- $type: haxework.gui.ButtonView
geometry.padding: [25, 8]
skin: $r:skin:button
text: red
+onPress: "$code:Theme.setColor(0xAA3333)"
- $type: haxework.gui.ButtonView
geometry.padding: [25, 8]
skin: $r:skin:button
text: yellow
+onPress: "$code:Theme.setColor(0xFFCC55, 0x555555)"
- $type: haxework.gui.ButtonView
geometry.padding: [25, 8]
skin: $r:skin:button
text: pink
+onPress: "$code:Theme.setColor(0xCC33AA)"
text: Color
+onPress: "$code:choiceColor()"
# separator
- $type: haxework.gui.SpriteView
geometry.size.stretch: true

10
demo/src/demo/Model.hx Normal file
View File

@@ -0,0 +1,10 @@
package demo;
typedef Model = {
var id:String;
var created_at:Int;
var maker:String;
var title:String;
var message:String;
var image:{url:String, width:Int, height:Int};
}

View File

@@ -13,9 +13,7 @@ class Theme {
public static function setColor(color:Int, textColor:Int = 0xffffff):Void {
var text:ISkin<Dynamic> = new TextSkin(textColor, 16, "Courirer");
var background:SkinSet = [
Skin.color(0x00000),
];
var background = Skin.color(0x00000);
var button:SkinSet = [
Skin.buttonColor(color),
text,
@@ -32,18 +30,22 @@ class Theme {
var scroll:SkinSet = [
ScrollBarSkin.vertical(color, ColorUtils.diff(color, 128)),
];
var border:SkinSet = [
Skin.border(ColorUtils.multiply(color, 1.5), 1, 2),
];
resources.skin.put("text", [
var border = Skin.border(ColorUtils.multiply(color, 1.5), 1, 2);
resources.skin.put("text0", [
Skin.color(ColorUtils.diff(color, 128)),
new TextSkin(ColorUtils.diff(color, -128), 16, "Courirer"),
]);
resources.skin.put("background", background);
resources.skin.put("text1", [
Skin.color(ColorUtils.diff(color, 64)),
new TextSkin(ColorUtils.diff(color, -128), 16, "Courirer"),
]);
resources.skin.put("text", resources.skin.get("text0"));
resources.skin.put("background", [background]);
resources.skin.put("button", button);
resources.skin.put("tab", tab);
resources.skin.put("view", view);
resources.skin.put("scroll", scroll);
resources.skin.put("border", border);
resources.skin.put("border", [border]);
resources.skin.put("panel", [background, border]);
}
}

View File

@@ -1,31 +1,17 @@
package demo.form;
import haxework.gui.DataView;
import haxework.gui.TextView;
import haxework.gui.VGroupView;
import haxework.net.JsonLoader;
import haxework.resources.IResources;
@:template class DataForm extends VGroupView {
@:view var data:DataView<String>;
@:provide var resources:IResources;
private function init() {
new JsonLoader().GET("http://umix.tv/channel/data2/renova.json")
.then(function(data:Array<Dynamic>) {
var values = data.map(function(item) return '${item.id}: ${item.message}');
this.data.data = Util.marray(values, 1);
})
.catchError(function(error) trace(error));
}
private function factory(index:Int, value:String):TextView {
private function factory(index:Int, value:Model):TextView {
var label = new TextView();
label.geometry.size.percent.width = 100;
label.geometry.margin = 1;
label.geometry.padding = 2;
resources.skin.bind("text", label, "skin");
label.text = value;
label.skinId = "text";
label.text = (value.title != null ? '${value.title}\n-\n' : '') + value.message;
return label;
}
}

View File

@@ -5,11 +5,12 @@ views:
geometry.size.stretch: true
view:
id: data
$type: haxework.gui.DataView<String>
$type: haxework.gui.DataView<Model>
layout:
$type: haxework.gui.layout.VerticalLayout
factory: $this:factory
geometry.size.width: 100%
data: $r:any:data
scroll:
$type: haxework.gui.list.VScrollBarView
skin: $r:skin:scroll

View File

@@ -1,23 +1,18 @@
package demo.form;
import haxework.gui.list.LabelListItem;
import haxework.gui.list.ListView.IListItemView;
import haxework.gui.list.VListView;
import haxework.gui.VGroupView;
import haxework.net.JsonLoader;
@:template class ListForm extends VGroupView {
@:view public var list(default, null):VListView<String>;
@:view public var list(default, null):VListView<Model>;
private function init() {
new JsonLoader().GET("http://umix.tv/channel/data2/renova.json")
.then(function(data:Array<Dynamic>) {
var values = data.map(function(item) return '${item.id}: ${item.message}');
list.data = Util.marray(values, 50);
})
.catchError(function(error) trace(error));
private function factory() {
return new LabelListItem(function(index:Int, value:Model) return '${index}. ${value.id}: ${value.title}');
}
private function onItemSelect(item:IListItemView<String>):Void {
trace('onItemSelect: ${item.data}');
private function onItemSelect(item:IListItemView<Model>):Void {
trace('onItemSelect: ${item.data.id}');
}
}

View File

@@ -1,12 +1,13 @@
---
views:
- id: list
$type: haxework.gui.list.VListView<String>
$type: haxework.gui.list.VListView<Model>
+onItemSelect: $this:onItemSelect
factory: { $class: haxework.gui.list.LabelListItem }
factory: $this:factory
geometry.size.stretch: true
scroll:
$type: haxework.gui.list.VScrollBarView
skin: $r:skin:scroll
geometry.size.height: 100%
geometry.size.width: 10
data: $r:any:data50

View File

@@ -1,24 +1,29 @@
package demo.form;
import haxework.gui.DataView;
import haxework.gui.HGroupView;
import haxework.gui.ImageView;
import haxework.gui.IView;
import haxework.gui.TextView;
import haxework.resources.IResources;
import haxework.gui.utils.DrawUtil.FillType;
@:template class TailForm extends HGroupView {
@:view public var data:DataView<String>;
@:provide var resources:IResources;
private function init() {
data.data = [for (i in 0...100) '${i}'];
private function factory(index:Int, value:Model):IView<Dynamic> {
var view:IView<Dynamic>;
if (value.image != null) {
var imageView = new ImageView();
imageView.skinId = "border";
imageView.fillType = FillType.CONTAIN;
imageView.imageUrl = value.image.url;
view = imageView;
} else {
var textView = new TextView();
textView.skinId = "view";
textView.text = '${value.id}\n${value.maker}';
view = textView;
}
private function factory(index:Int, value:String):TextView {
var view = new TextView();
view.geometry.size.fixed.width = 100 + 100 * Math.random();
view.geometry.size.fixed.height = 100 + 100 * Math.random();
resources.skin.bind("view", view, "skin");
view.text = 'View #${index}';
view.geometry.size.fixed.width = 350;
view.geometry.size.fixed.height = 200;
return view;
}
}

View File

@@ -4,12 +4,13 @@ views:
geometry.size.stretch: true
view:
id: data
$type: haxework.gui.DataView<String>
$type: haxework.gui.DataView<Model>
layout:
$type: haxework.gui.layout.TailLayout
margin: 2
factory: $this:factory
geometry.size.width: 100%
data: $r:any:data
scroll:
$type: haxework.gui.list.VScrollBarView
skin: $r:skin:scroll

View File

@@ -0,0 +1,15 @@
package demo.popup;
import haxework.gui.ButtonView;
import haxework.gui.popup.PopupView;
import haxework.gui.skin.Skin;
@:template class ColorPopup extends PopupView<Int> {
private function colorViewFactory(index:Int, color:Int) {
var view = new ButtonView();
view.geometry.size.fixed = [48, 48];
view.skin = [Skin.buttonColor(color)];
return view;
}
}

View File

@@ -0,0 +1,35 @@
---
view:
$type: haxework.gui.VGroupView
geometry.size.width: 400
geometry.size.height: 200
geometry.padding: 10
geometry.hAlign: center
geometry.vAlign: middle
skinId: panel
views:
- id: colors
$type: haxework.gui.DataView
geometry.size.stretch: true
layout:
$type: haxework.gui.layout.TailLayout
vAlign: middle
margin: 5
factory: $this:colorViewFactory
data:
- 0x33AA33
- 0xAA3333
- 0xFFCC55 # 0x555555
- 0xCC33AA
- 0x3333AA
+onDataSelect: $this:close
- $type: haxework.gui.HGroupView
geometry.size.width: 100%
layout.hAlign: right
layout.margin: 10
views:
- $type: haxework.gui.ButtonView
geometry.padding: [25, 8]
skinId: button
text: Cancel
+onPress: $code:reject('cancel')

35
src/main/haxework/App.hx Normal file
View File

@@ -0,0 +1,35 @@
package haxework;
import haxework.gui.Root;
import haxework.gui.IView;
import flash.Lib;
import haxework.animate.Animate;
import haxework.animate.FadeAnimate;
import haxework.animate.UnFadeAnimate;
import haxework.gui.popup.PopupManager;
import haxework.net.manage.ILoaderManager;
import haxework.net.manage.LoaderManager;
import haxework.resources.IResources;
import haxework.resources.Resources;
class App {
@:provide var app:App;
@:provide var resources:IResources;
@:provide var loaderManager:ILoaderManager;
@:provide var popupManager:PopupManager;
public function new() {
resources = new Resources();
loaderManager = new LoaderManager();
popupManager = new PopupManager();
popupManager.showAnimateFactory = UnFadeAnimate;
popupManager.closeAnimateFactory = FadeAnimate;
app = this;
}
public function start(view:IView<Dynamic>) {
Animate.bind(Lib.current.stage);
Root.bind(view);
}
}

View File

@@ -4,6 +4,7 @@ import flash.events.Event;
import flash.display.Stage;
class Animate implements IAnimate {
public static var defaultDuraion = 300;
private static var running:Array<IAnimate> = new Array<IAnimate>();
@@ -26,10 +27,10 @@ class Animate implements IAnimate {
private var progress:Float;
public function new(duration:Int) {
this.duration = duration;
this.duration = duration > -1 ? duration : defaultDuraion;
}
public function start(callback:IAnimate -> Void, ?custom:Bool = false):Void {
public function start(callback:IAnimate -> Void, custom:Bool = false):Void {
startTime = Date.now().getTime();
this.callback = callback;
if (!custom) running.push(this);

View File

@@ -1,26 +1,25 @@
package haxework.animate;
import flash.display.DisplayObjectContainer;
import haxework.animate.IAnimate;
import flash.display.Sprite;
import haxework.gui.IView;
import haxework.animate.Animate;
import haxework.animate.IAnimate;
import haxework.gui.IView;
class CircleMaskAnimate extends Animate {
private var view:IView;
private var view:IView<Dynamic>;
private var mask:Sprite;
private var cyrcle:Sprite;
private var size:Float;
public function new(view:IView, ?duration:Int = 1000) {
public function new(view:IView<Dynamic>, duration:Int = -1) {
super(duration);
this.view = view;
this.mask = new Sprite();
this.cyrcle = new Sprite();
}
override public function start(callback:IAnimate -> Void, ?custom:Bool = false):Void {
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);
@@ -44,7 +43,7 @@ class CircleMaskAnimate extends Animate {
mask.graphics.endFill();
cyrcle.graphics.clear();
cyrcle.graphics.lineStyle(8, 0xffffff);
cyrcle.graphics.lineStyle(4, 0xffffff);
cyrcle.graphics.drawCircle(size + size / 2, size + size / 2, r / 2);
cyrcle.graphics.lineStyle();
}

View File

@@ -1,20 +1,20 @@
package haxework.animate;
import flash.display.DisplayObject;
import haxework.animate.IAnimate;
import flash.display.Sprite;
import haxework.gui.IView;
import haxework.animate.Animate;
class FadeAnimate extends Animate {
private var view:IView;
private var view:IView<DisplayObject>;
public function new(view:IView, ?duration = 500) {
public function new(view:IView<DisplayObject>, duration:Int = -1) {
super(duration);
this.view = view;
}
override public function start(callback:IAnimate -> Void, ?custom:Bool = false):Void {
override public function start(callback:IAnimate -> Void, custom:Bool = false):Void {
view.content.alpha = 1.0;
super.start(callback, custom);
}

View File

@@ -2,7 +2,8 @@ package haxework.animate;
interface IAnimate {
public function start(callback:IAnimate->Void, ?custom:Bool = false):Void;
public function start(callback:IAnimate -> Void, custom:Bool = false):Void;
public function cancel():Void;
private function update(time:Float):Void;

View File

@@ -0,0 +1,24 @@
package haxework.animate;
import flash.display.DisplayObject;
import haxework.gui.IView;
class SlideAnimate extends Animate {
private var view:IView<DisplayObject>;
public function new(view:IView<DisplayObject>, duration:Int = -1) {
super(duration);
this.view = view;
}
override public function start(callback:IAnimate -> Void, custom:Bool = false):Void {
view.content.x = view.x - this.view.width + this.view.width / progress;
super.start(callback, custom);
}
override private function update(time:Float):Void {
super.update(time);
view.content.x = view.x - this.view.width + this.view.width / Math.min(1, progress);
}
}

View File

@@ -1,19 +1,19 @@
package haxework.animate;
import flash.display.Sprite;
import flash.display.DisplayObject;
import haxework.gui.IView;
import haxework.animate.Animate;
class UnFadeAnimate extends Animate {
private var view:IView;
private var view:IView<DisplayObject>;
public function new(view:IView, ?duration = 500) {
public function new(view:IView<DisplayObject>, duration:Int = -1) {
super(duration);
this.view = view;
}
override public function start(callback:IAnimate -> Void, ?custom:Bool = false):Void {
override public function start(callback:IAnimate -> Void, custom:Bool = false):Void {
view.content.alpha = 0.0;
super.start(callback, custom);
}

View File

@@ -1,5 +0,0 @@
package haxework.core;
interface IDisposable {
public function dispose():Void;
}

View File

@@ -1,133 +0,0 @@
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 + ")";
}
}

View File

@@ -1,57 +0,0 @@
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;
content.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;
}
}

View File

@@ -4,10 +4,12 @@ import flash.display.DisplayObject;
import flash.events.MouseEvent;
import haxework.signal.Signal;
typedef Factory<D> = Int -> D -> IView<Dynamic>
class DataView<D> extends GroupView {
public var data(default, set):Array<D>;
public var factory(default, default):Int -> D -> IView<Dynamic>;
public var factory(default, set):Factory<D>;
public var onItemSelect(default, null):Signal3<Int, D, IView<Dynamic>> = new Signal3();
public var onDataSelect(default, null):Signal<D> = new Signal();
@@ -15,10 +17,16 @@ class DataView<D> extends GroupView {
private function set_data(value:Array<D>):Array<D> {
data = value;
rebuild();
if (factory != null) rebuild();
return data;
}
private function set_factory(value:Factory<D>):Factory<D> {
factory = value;
if (data != null) rebuild();
return factory;
}
private function rebuild():Void {
for (view in views) {
view.content.removeEventListener(MouseEvent.CLICK, onItemClick);

View File

@@ -17,6 +17,7 @@ interface IView<C:DisplayObject> {
public var content(default, null):C;
public var skin(default, set):SkinSet;
public var skinId(null, set):String;
public var parent(default, null):Null<IGroupView>;

View File

@@ -2,6 +2,7 @@ package haxework.gui;
import flash.display.BitmapData;
import haxework.gui.skin.BitmapSkin;
import haxework.gui.skin.ISkin;
import haxework.gui.utils.DrawUtil.FillType;
import haxework.net.ImageLoader;
@@ -9,22 +10,29 @@ class ImageView extends SpriteView {
public var image(default, set):BitmapData;
public var imageUrl(default, set):String;
public var fillType(default, set):FillType;
public function new(?image:BitmapData) {
private var bitmapSkin:BitmapSkin = new BitmapSkin();
public function new(image:BitmapData = null) {
super();
fillType = FillType.DEFAULT;
if (image != null) {
this.image = image;
}
}
override private function set_skin(value:SkinSet):SkinSet {
value = value.slice(0);
value.unshift(bitmapSkin);
return super.set_skin(value);
}
private function set_image(value:BitmapData):BitmapData {
if (image != value) {
image = value;
skin = [new BitmapSkin(image, FillType.DEFAULT)];
//geometry.size.content.width = value.width;
//geometry.size.content.height = value.height;
//toUpdate();
//toRedraw();
bitmapSkin.image = value;
toRedraw();
}
return image;
}
@@ -34,8 +42,15 @@ class ImageView extends SpriteView {
imageUrl = value;
new ImageLoader().GET(imageUrl).then(function(data) {
image = data;
});
}).catchError(function(e) L.w("ImageView", "load", e));
}
return imageUrl;
}
private function set_fillType(value:FillType):FillType {
if (fillType != value) {
bitmapSkin.fillType = fillType = value;
}
return fillType;
}
}

View File

@@ -7,10 +7,9 @@ import flash.errors.Error;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.Lib;
import haxework.core.IDisposable;
import haxework.signal.Signal;
class Root implements IDisposable {
class Root {
public static function bind(view:IView<Dynamic>, autoSize:Bool = true) {
new Root(view, autoSize);

View File

@@ -144,7 +144,7 @@ class TextView extends SpriteView implements ITextView {
private function updateTextSize():Void {
var size = TextUtil.getSize(textField);
setContentSize(size.x, size.y);
setContentSize(size.x, size.y, "text");
}
override public function update():Void {
@@ -168,7 +168,7 @@ class TextView extends SpriteView implements ITextView {
private function placeTextField(textField:TextField):Void {
textField.width = width;
textField.height = geometry.size.content.height;
textField.height = geometry.size.content.exists("text") ? geometry.size.content.get("text").height : height;
textField.x = switch (layout.hAlign) {
case LEFT | NONE: geometry.padding.left;
@@ -178,8 +178,8 @@ class TextView extends SpriteView implements ITextView {
}
textField.y = switch (layout.vAlign) {
case TOP | NONE: geometry.padding.top;
case MIDDLE: (height - geometry.size.content.height) / 2 + geometry.padding.top - geometry.padding.bottom;
case BOTTOM: height - geometry.size.content.height - geometry.padding.bottom;
case MIDDLE: (height - textField.height) / 2 + geometry.padding.top - geometry.padding.bottom;
case BOTTOM: height - textField.height - geometry.padding.bottom;
default: 0;
}
}

View File

@@ -1,12 +1,14 @@
package haxework.gui;
import haxework.gui.skin.ISkin.ISizeSkin;
import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import haxework.gui.core.Geometry;
import haxework.gui.skin.ISkin.ISizeSkin;
import haxework.gui.skin.ISkin.SkinSet;
import haxework.resources.IResources;
class View<C:DisplayObject> implements IView<C> {
@:provide private var r:IResources;
private static var counter:Int = 0;
public static var updater(default, null):ViewUpdater = new ViewUpdater();
@@ -23,6 +25,7 @@ class View<C:DisplayObject> implements IView<C> {
public var content(default, null):C;
public var skin(default, set):SkinSet;
public var skinId(null, set):String;
public var parent(default, null):Null<IGroupView>;
@@ -63,22 +66,16 @@ class View<C:DisplayObject> implements IView<C> {
for (skin in this.skin) {
if (Std.is(skin, ISizeSkin)) {
var sizeSkin:ISizeSkin = cast skin;
setSize(sizeSkin.width, sizeSkin.height);
setContentSize(sizeSkin.width, sizeSkin.height, "skin");
}
skin.draw(this);
}
}
private function setSize(width:Float, height:Float):Void {
if (width != geometry.size.fixed.width || height != geometry.size.fixed.height) {
geometry.size.fixed = [width, height];
toUpdateParent();
}
}
private function setContentSize(width:Float, height:Float):Void {
if (width != geometry.size.content.width || height != geometry.size.content.height) {
geometry.size.content = [width, height];
private function setContentSize(width:Float, height:Float, type:String="default"):Void {
var contentSize = geometry.size.content.get(type);
if (contentSize == null || width != contentSize.width || height != contentSize.height) {
geometry.size.content.set(type, [width, height]);
toUpdateParent();
}
}
@@ -123,6 +120,11 @@ class View<C:DisplayObject> implements IView<C> {
return this.skin;
}
private function set_skinId(value:String):String {
r.skin.bind(value, this, "skin");
return value;
}
private function set_visible(value:Bool):Bool {
if (visible != value) {
visible = value;

View File

@@ -29,7 +29,7 @@ abstract ASizeValue(SizeValue) {
}
class SizeSet {
public var content(default, default):Size;
public var content(default, default):Map<String, Size>;
public var fixed(default, default):Size;
public var percent(default, default):Size;
public var stretch(null, set):Bool;
@@ -38,7 +38,7 @@ class SizeSet {
public var height(null, set):ASizeValue;
public function new() {
this.content = [];
this.content = new Map();
this.fixed = [];
this.percent = [];
}
@@ -96,7 +96,9 @@ class Geometry {
}
var result = size.fixed.width;
if (result < 0) {
result = size.content.width;
for (s in size.content.iterator()) {
result = Math.max(result, s.width);
}
}
result += padding.horizontal;
return FIXED(result);
@@ -108,7 +110,9 @@ class Geometry {
}
var result = size.fixed.height;
if (result < 0) {
result = size.content.height;
for (s in size.content.iterator()) {
result = Math.max(result, s.height);
}
}
result += padding.vertical;
return FIXED(result);

View File

@@ -5,7 +5,7 @@ import haxework.animate.IAnimate;
import haxework.gui.IView;
import haxework.gui.GroupView;
class FrameSwitcher extends GroupView implements IFrameSwitcher {
class FrameSwitcher extends GroupView {
public var current(default, null):Null<IView<Dynamic>>;
public var onSwitch:Signal<IView<Dynamic>> = new Signal();
@@ -39,6 +39,7 @@ class FrameSwitcher extends GroupView implements IFrameSwitcher {
throw 'frame "$id" not found';
}
addView(current);
update();
//ToDo:
if (content.stage != null) content.stage.focus = cast(current, SpriteView).content;
var onShowMethod:Dynamic = Reflect.field(current, "onShow");

View File

@@ -1,8 +0,0 @@
package haxework.gui.frame;
import haxework.gui.IView;
interface IFrameSwitcher extends IView<Dynamic> {
public var current(default, null):Null<IView<Dynamic>>;
public function change(id:String):IView<Dynamic>;
}

View File

@@ -29,8 +29,7 @@ class HorizontalLayout extends DefaultLayout {
maxSize = Math.max(maxSize, view.height);
}
group.geometry.size.content.height = maxSize;
group.geometry.size.content.width = fixedSize;
group.geometry.size.content.set("group", [fixedSize, maxSize]);
leftSize -= fixedSize;
for (view in views) {

View File

@@ -1,5 +1,6 @@
package haxework.gui.layout;
import haxework.gui.core.VAlign;
typedef Row = {
var width:Float;
var height:Float;
@@ -50,10 +51,18 @@ class TailLayout extends DefaultLayout {
var y:Float = Math.max(group.geometry.padding.top, (group.height - h) / 2);
y = group.geometry.padding.top;
if (h < group.height) {
y = switch vAlign {
case TOP | NONE: group.geometry.padding.top;
case MIDDLE: (group.height - h) / 2;
case BOTTOM: group.height - h - group.geometry.padding.bottom;
}
}
for (row in rows) {
placeRow(group, y, row);
y += row.height + margin;
}
group.geometry.size.content.height = h;
group.geometry.size.content.set("group", [-1, h]);
}
}

View File

@@ -25,8 +25,7 @@ class VerticalLayout extends DefaultLayout {
maxSize = Math.max(maxSize, view.width);
}
group.geometry.size.content.width = maxSize;
group.geometry.size.content.height = fixedSize;
group.geometry.size.content.set("group", [maxSize, fixedSize]);
leftSize -= fixedSize;
for (view in views) {

View File

@@ -2,24 +2,32 @@ package haxework.gui.list;
import haxework.gui.core.HAlign;
import haxework.gui.list.ListView.IListItemView;
import haxework.gui.skin.ColorSkin;
private typedef Formatter<T> = Int -> T -> String;
class LabelListItem<T> extends LabelView implements IListItemView<T> {
public var item_index(default, default):Int;
public var data(default, set):T;
public var formatter(default, default):Formatter<T>;
public function new() {
private static function defaultFormatter<T>(index:Int, value:T):String {
return Std.string(value);
}
public function new(formatter:Formatter<T> = null) {
super();
this.formatter = formatter == null ? defaultFormatter : formatter;
geometry.size.percent.width = 100;
geometry.size.fixed.height = 20;
geometry.padding = 8;
layout.hAlign = HAlign.LEFT;
layout.hAlign = LEFT;
}
private function set_data(value:T):T {
data = value;
text = Std.string(value);
skin = item_index % 2 == 1 ? [new ColorSkin(0xdddddd)] : [new ColorSkin(0xcccccc)];
text = formatter(item_index, value);
skinId = 'text${item_index % 2}';
return value;
}
}

View File

@@ -12,7 +12,7 @@ import haxework.utils.NumberUtil;
class ListView<D> extends GroupView {
public var data(default, set):Array<D>;
public var factory(null, default):Class<IListItemView<D>>;
public var factory(null, default):Void->IListItemView<D>;
public var offset(default, set):Int;
private var offsetDiff(default, set):Float;
@@ -183,7 +183,7 @@ class ListView<D> extends GroupView {
override public function update():Void {
super.update();
recalcSize(Type.createInstance(factory, []));
recalcSize(factory());
render();
}
@@ -195,7 +195,7 @@ class ListView<D> extends GroupView {
var diff:Int = size - items.length;
if (diff > 0) {
for (i in 0...diff) {
var item:IListItemView<D> = Type.createInstance(factory, []);
var item:IListItemView<D> = factory();
items.push(item);
setClickListener(item);
box.addView(item);

View File

@@ -4,36 +4,35 @@ import haxework.animate.IAnimate;
import haxework.gui.Root;
import haxework.gui.IGroupView;
typedef P = PopupView<Dynamic>;
class PopupManager {
public var showAnimateFactory(default, default):Class<IAnimate>;
public var closeAnimateFactory(default, default):Class<IAnimate>;
private var popups:Array<PopupView<Dynamic>>;
private var popups:Array<P>;
public function new() {
popups = new Array<PopupView<Dynamic>>();
popups = new Array<P>();
}
public function show(popup:PopupView<Dynamic>):Void {
public function show(popup:P):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 {
public function close(popup:P):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();
}
}

View File

@@ -1,55 +1,48 @@
package haxework.gui.popup;
import promhx.Deferred;
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.core.Geometry.Position;
import haxework.gui.GroupView;
import haxework.gui.skin.Skin;
import promhx.Deferred;
import promhx.Promise;
class PopupView<V:IView> extends GroupView {
class PopupView<R> extends GroupView {
@:provide var manager:PopupManager;
private var buttonId:String;
private var contentView:V;
private var deferred:Deferred<String>;
public var view(default, set):IView<Dynamic>;
private var deferred:Deferred<R>;
public function new(contentViewFactory:Class<V>) {
public function new() {
super();
pWidth = 100;
pHeight = 100;
inLayout = false;
skin = new ColorSkin(0x000000, 0.6);
this.contentView = Type.createInstance(contentViewFactory, [{listener:this}]);
addView(contentView);
geometry.size.stretch = true;
geometry.position = Position.ABSOLUTE;
skin = [Skin.color(0x000000, 0.6)];
}
public function onPress(button:ButtonView) {
this.buttonId = button.id;
close();
private function set_view(value:IView<Dynamic>):IView<Dynamic> {
this.view = value;
this.views = [value];
return this.view;
}
public function show():Deferred<String> {
Provider.get(PopupManager).show(this);
deferred = new Deferred<String>();
return deferred;
public function show():Promise<R> {
manager.show(this);
deferred = new Deferred<R>();
return deferred.promise();
}
public function close():Void {
Provider.get(PopupManager).close(this);
}
public function onShow():Void {
buttonId = "close";
}
public function onClose():Void {
public function close(result:R):Void {
manager.close(this);
if (deferred != null) {
deferred.resolve(buttonId);
deferred.resolve(result);
deferred = null;
}
}
public function reject(reason:Dynamic):Void {
manager.close(this);
if (deferred != null) {
deferred.throwError(reason);
deferred = null;
}
}

View File

@@ -32,6 +32,6 @@ class BitmapSkin implements ISkin<SpriteView> implements ISizeSkin {
public function draw(view:SpriteView):Void {
if (image == null) return;
DrawUtil.draw(view.content.graphics, image, new Rectangle(0, 0, view.width, view.height), fillType, color);
DrawUtil.draw(view.content.graphics, image, new Rectangle(0, 0, view.width, view.height), fillType, color, false);
}
}

View File

@@ -14,7 +14,7 @@ class BorderSkin implements ISkin<SpriteView> {
public function draw(view:SpriteView):Void {
view.content.graphics.lineStyle(tickness, color, alpha, true);
view.content.graphics.drawRect(tickness, tickness, view.width - tickness * 2, view.height - tickness * 2);
view.content.graphics.drawRect(tickness / 2, tickness / 2, view.width - tickness, view.height - tickness);
view.content.graphics.lineStyle();
}
}

View File

@@ -39,7 +39,7 @@ class BitmapUtil {
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);
out.applyFilter(out, out.rect, new Point(0, 0), cmf);
toCache(image, "grayscale:" + m, out);
return out;
}

View File

@@ -1,18 +0,0 @@
package haxework.net.order;
import promhx.Promise;
import promhx.Deferred;
typedef Order<T> = {
var id:String;
var data:Null<T>;
var deferred:Deferred<T>;
var clients:Int;
}
interface IOrderSupplier {
public var orders(default, null):Map<String, Order<Dynamic>>;
public function request<T>(url:String, clazz:Class<T>):Promise<T>;
public function release(url:String, force:Bool = false):Void;
}

View File

@@ -1,82 +0,0 @@
package haxework.net.order;
import promhx.Promise;
import promhx.Deferred;
import haxework.net.order.IOrderSupplier.Order;
import flash.display.BitmapData;
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>):Promise<T> {
if (orders.exists(url)) {
var order:Order<T> = orders.get(url);
order.clients++;
//L.d(TAG, "Request(" + order.clients + "): " + url);
return order.deferred.promise();
} else {
var deferred = new Deferred<T>();
var order:Order<T> = {
id:url,
data:null,
deferred:deferred,
clients:1
}
//L.d(TAG, "Request(" + order.clients + "): " + url);
orders.set(url, order);
var loader:ILoader<T> = buildLoader(clazz);
loader.GET(url)
.then(function(data:T):Void {
if (orders.exists(url)) {
var order:Order<T> = orders.get(url);
order.data = data;
order.deferred.resolve(data);
}
})
.catchError(function(error:Dynamic):Void {
if (orders.exists(url)) orders.get(url).deferred.throwError(error);
orders.remove(url);
});
return deferred.promise();
}
}
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 (data != null && 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> = cast 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"));
}
}

View File

@@ -1,9 +1,9 @@
package haxework.resources;
import haxework.gui.skin.ISkin.SkinSet;
import flash.display.MovieClip;
import haxework.resources.Resources.ResMap;
import flash.display.BitmapData;
import flash.display.MovieClip;
import haxework.gui.skin.ISkin.SkinSet;
import haxework.resources.Resources.ResMap;
interface IResources {
public var image(default, null):ResMap<BitmapData>;

View File

@@ -1,20 +1,19 @@
package haxework.resources;
import haxework.gui.skin.ISkin.SkinSet;
import flash.display.BitmapData;
import flash.display.MovieClip;
import haxe.ds.StringMap;
import haxework.core.Tuple;
import haxework.gui.skin.ISkin;
private typedef F = Tuple2<Dynamic, String>
private typedef Listener = {object:Dynamic, field:String};
class ResMap<T> extends StringMap<T> {
private var listeners:Map<String, Array<F>>;
private var listeners:StringMap<Array<Listener>>;
public function new() {
super();
listeners = new Map<String, Array<F>>();
listeners = new StringMap();
}
public function put(key:String, value:T):Void {
@@ -25,17 +24,18 @@ class ResMap<T> extends StringMap<T> {
}
public function bind(key:String, object:Dynamic, field:String):Void {
var f:F = Tuple.two(object, field);
var listener:Listener = {object:object, field:field};
if (listeners.exists(key)) {
listeners.get(key).push(f);
listeners.set(key, listeners.get(key).filter(function(l) return l.object != object || l.field != field));
listeners.get(key).push(listener);
} else {
listeners.set(key, [f]);
listeners.set(key, [listener]);
}
if (exists(key)) call(f, get(key));
if (exists(key)) call(listener, get(key));
}
private function call(field:F, value:T):Void {
Reflect.setProperty(field.first, field.second, value);
private function call(listener:Listener, value:T):Void {
Reflect.setProperty(listener.object, listener.field, value);
}
public function merge(value:Dynamic<T>):Void {