├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
└── workflows
│ ├── build.yml
│ └── project
│ ├── build.xml
│ └── src
│ └── Main.hx
├── .gitignore
├── LICENSE.md
├── README.md
├── classpath.exclusions
├── extraParams.hxml
├── haxe
└── ui
│ └── backend
│ ├── AppImpl.hx
│ ├── AssetsImpl.hx
│ ├── BackendImpl.hx
│ ├── CallLaterImpl.hx
│ ├── ComponentGraphicsImpl.hx
│ ├── ComponentImpl.hx
│ ├── ComponentSurface.hx
│ ├── EventImpl.hx
│ ├── FontData.hx
│ ├── ImageData.hx
│ ├── ImageDisplayImpl.hx
│ ├── ImageSurface.hx
│ ├── OpenFileDialogImpl.hx
│ ├── PlatformImpl.hx
│ ├── SaveFileDialogImpl.hx
│ ├── ScreenImpl.hx
│ ├── TextDisplayImpl.hx
│ ├── TextInputImpl.hx
│ ├── TimerImpl.hx
│ ├── ToolkitOptions.hx
│ ├── flixel
│ ├── CursorHelper.hx
│ ├── FlxHaxeUIAppState.hx
│ ├── FlxStyleHelper.hx
│ ├── InputManager.hx
│ ├── KeyboardHelper.hx
│ ├── MouseHelper.hx
│ ├── OpenFLStyleHelper.hx
│ ├── StateHelper.hx
│ ├── UIFragment.hx
│ ├── UIFragmentBase.hx
│ ├── UIRTTITools.hx
│ ├── UIRuntimeFragment.hx
│ ├── UIRuntimeState.hx
│ ├── UIRuntimeSubState.hx
│ ├── UIState.hx
│ ├── UIStateBase.hx
│ ├── UISubState.hx
│ ├── UISubStateBase.hx
│ ├── _module
│ │ └── styles
│ │ │ └── default
│ │ │ ├── cursors
│ │ │ └── pointer.png
│ │ │ └── main.css
│ ├── components
│ │ ├── SparrowPlayer.hx
│ │ └── SpriteWrapper.hx
│ ├── macros
│ │ └── UIStateMacro.hx
│ └── textinputs
│ │ ├── FlxTextInput.hx
│ │ └── OpenFLTextInput.hx
│ └── module.xml
├── haxelib.json
└── include.xml
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [ianharrigan]
2 | patreon: haxeui
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Expected Behavior
4 |
5 |
6 |
7 | ## Current Behavior
8 |
9 |
10 |
11 | ## Possible Solution
12 |
13 |
14 |
15 | ## Steps to Reproduce (for bugs)
16 |
17 |
18 | 1.
19 | 2.
20 | 3.
21 | 4.
22 |
23 | ## Media
24 |
25 |
26 | ## Test app / minimal test case
27 |
28 |
29 |
30 | ## Context
31 |
32 |
33 |
34 | ## Your Environment
35 |
36 | * Version used:
37 | * Environment name and version (e.g. Chrome 39, node.js 5.4):
38 | * Operating System and version (desktop or mobile):
39 | * Link to your project:
40 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on: [push, repository_dispatch]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 |
9 | strategy:
10 | matrix:
11 | os: [ubuntu-latest, macos-13, windows-latest]
12 | haxe-version: [4.2.5, 4.3.0, 4.3.1]
13 | target: [html5, linux, windows, mac]
14 | exclude:
15 | - os: ubuntu-latest
16 | target: windows
17 | - os: ubuntu-latest
18 | target: mac
19 | - os: windows-latest
20 | target: linux
21 | - os: windows-latest
22 | target: mac
23 | - os: macos-13
24 | target: linux
25 | - os: macos-13
26 | target: windows
27 |
28 |
29 | steps:
30 | - uses: actions/checkout@v1
31 | - name: Setup Haxe (${{ matrix.target }}, haxe ${{ matrix.haxe-version }}, ${{ matrix.os }})
32 | uses: krdlab/setup-haxe@v1
33 | with:
34 | haxe-version: ${{ matrix.haxe-version }}
35 |
36 | - name: Setup app (${{ matrix.target }}, haxe ${{ matrix.haxe-version }}, ${{ matrix.os }})
37 | run: |
38 | git clone --branch master https://github.com/haxeui/haxeui-core.git --depth=1
39 | haxelib dev haxeui-core haxeui-core
40 | haxelib dev haxeui-flixel .
41 | haxelib install hxcpp --always --quiet
42 | haxelib install actuate --always --quiet
43 | haxelib install openfl --always --quiet
44 | haxelib install flixel --always --quiet
45 | echo "y" | haxelib run openfl setup
46 |
47 | - name: Build app (${{ matrix.target }}, haxe ${{ matrix.haxe-version }}, ${{ matrix.os }})
48 | run: |
49 | cd .github/workflows/project
50 | haxelib run openfl build ${{ matrix.target }}
51 |
--------------------------------------------------------------------------------
/.github/workflows/project/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.github/workflows/project/src/Main.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 | class Main {
4 | public static function main() {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .classpath
16 | .settings/
17 | .loadpath
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # CDT-specific
26 | .cproject
27 |
28 | # PDT-specific
29 | .buildpath
30 |
31 |
32 | #################
33 | ## Visual Studio
34 | #################
35 |
36 | ## Ignore Visual Studio temporary files, build results, and
37 | ## files generated by popular Visual Studio add-ons.
38 |
39 | # User-specific files
40 | *.suo
41 | *.user
42 | *.sln.docstates
43 |
44 | # Build results
45 | *_i.c
46 | *_p.c
47 | *.ilk
48 | *.meta
49 | *.obj
50 | *.pch
51 | *.pdb
52 | *.pgc
53 | *.pgd
54 | *.rsp
55 | *.sbr
56 | *.tlb
57 | *.tli
58 | *.tlh
59 | *.tmp
60 | *.vspscc
61 | .builds
62 | *.dotCover
63 |
64 | ## TODO: If you have NuGet Package Restore enabled, uncomment this
65 | #packages/
66 |
67 | # Visual C++ cache files
68 | ipch/
69 | *.aps
70 | *.ncb
71 | *.opensdf
72 | *.sdf
73 |
74 | # Visual Studio profiler
75 | *.psess
76 | *.vsp
77 |
78 | # ReSharper is a .NET coding add-in
79 | _ReSharper*
80 |
81 | # Installshield output folder
82 | [Ee]xpress
83 |
84 | # DocProject is a documentation generator add-in
85 | DocProject/buildhelp/
86 | DocProject/Help/*.HxT
87 | DocProject/Help/*.HxC
88 | DocProject/Help/*.hhc
89 | DocProject/Help/*.hhk
90 | DocProject/Help/*.hhp
91 | DocProject/Help/Html2
92 | DocProject/Help/html
93 |
94 | # Click-Once directory
95 | publish
96 |
97 | # Others
98 | [Bb]in
99 | [Oo]bj
100 | sql
101 | TestResults
102 | *.Cache
103 | ClientBin
104 | stylecop.*
105 | ~$*
106 | *.dbmdl
107 | Generated_Code #added for RIA/Silverlight projects
108 |
109 | # Backup & report files from converting an old project file to a newer
110 | # Visual Studio version. Backup files are not needed, because we have git ;-)
111 | _UpgradeReport_Files/
112 | Backup*/
113 | UpgradeLog*.XML
114 |
115 |
116 |
117 | ############
118 | ## Windows
119 | ############
120 |
121 | # Windows image file caches
122 | Thumbs.db
123 |
124 | # Folder config file
125 | Desktop.ini
126 |
127 |
128 | #############
129 | ## Python
130 | #############
131 |
132 | *.py[co]
133 |
134 | # Packages
135 | *.egg
136 | *.egg-info
137 | dist
138 | build
139 | eggs
140 | parts
141 | bin
142 | var
143 | sdist
144 | develop-eggs
145 | .installed.cfg
146 |
147 | # Installer logs
148 | pip-log.txt
149 |
150 | # Unit test / coverage reports
151 | .coverage
152 | .tox
153 |
154 | #Translations
155 | *.mo
156 |
157 | #Mr Developer
158 | .mr.developer.cfg
159 |
160 | # Mac crap
161 | .DS_Store
162 |
163 |
164 | *.backup
165 | dox.xml
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Ian Harrigan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # haxeui-flixel
4 | `haxeui-flixel` is the `Flixel` backend for `HaxeUI`.
5 |
6 |
7 | ## Installation
8 | `haxeui-flixel` relies on `haxeui-core` as well as `Flixel`. To install:
9 |
10 | ```
11 | haxelib install flixel
12 | haxelib install haxeui-core
13 | haxelib install haxeui-flixel
14 | ```
15 |
16 | ## Usage
17 |
18 | After installing `Lime`, `OpenFL`, `Flixel`, `haxeui-core`, and `haxeui-flixel`, the latter three should be included in `project.xml`. In the future, including `haxeui-flixel` will also handle the dependencies automatically.
19 |
20 | ```xml
21 |
22 |
23 |
24 | ```
25 |
26 | ### Toolkit initialization and usage
27 | Before you start using `HaxeUI` in your project, you must first initialize the `Toolkit`.
28 |
29 | ```haxe
30 | Toolkit.init();
31 | ```
32 |
33 | Once the toolkit is initialized, you can add components using the methods specified here.
34 |
35 | ```haxe
36 | var app = new HaxeUIApp();
37 | app.ready(
38 | function() {
39 | var main = ComponentMacros.buildComponent("assets/xml/test.xml"); // whatever your XML layout path is
40 | app.addComponent(main);
41 | app.start();
42 | }
43 | );
44 | ```
45 |
46 | Some examples are [here](https://github.com/haxeui/component-examples).
47 |
48 | ## Addtional resources
49 | * component-explorer - Browse HaxeUI components
50 | * playground - Write and test HaxeUI layouts in your browser
51 | * component-examples - Various componet examples
52 | * haxeui-api - The HaxeUI api docs.
53 | * haxeui-guides - Set of guides to working with HaxeUI and backends.
54 |
--------------------------------------------------------------------------------
/classpath.exclusions:
--------------------------------------------------------------------------------
1 | ; exclude paths from classpath when searching for haxeui arifacts (module.xml, native.xml, etc)
2 | ; speeds up build
3 | \/flixel\/
4 | \/lime\/.*$
5 | \/openfl\/.*$
6 | \/actuate\/.*$
--------------------------------------------------------------------------------
/extraParams.hxml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haxeui/haxeui-flixel/100f2c96beab619cfe72c567a058c41c71e3e998/extraParams.hxml
--------------------------------------------------------------------------------
/haxe/ui/backend/AppImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import flixel.FlxGame;
4 | import haxe.ui.backend.flixel.FlxHaxeUIAppState;
5 | import lime.graphics.Image;
6 | import openfl.Lib;
7 |
8 | class AppImpl extends AppBase {
9 | public function new() {
10 | }
11 |
12 | private override function build() {
13 | var targetFramerate = Toolkit.backendProperties.getPropInt("haxe.ui.flixel.fps", 60);
14 |
15 | #if (flixel < "5.0.0")
16 | Lib.current.stage.addChild(new FlxGame(0, 0, FlxHaxeUIAppState, 1, targetFramerate, targetFramerate, true));
17 | #else
18 | Lib.current.stage.addChild(new FlxGame(0, 0, FlxHaxeUIAppState, targetFramerate, targetFramerate, true));
19 | #end
20 | if (Toolkit.backendProperties.getPropBool("haxe.ui.flixel.fps.show")) {
21 | var x = Toolkit.backendProperties.getPropInt("haxe.ui.flixel.fps.left");
22 | var y = Toolkit.backendProperties.getPropInt("haxe.ui.flixel.fps.top");
23 | var c = Toolkit.backendProperties.getPropCol("haxe.ui.flixel.fps.color");
24 | Lib.current.stage.addChild(new openfl.display.FPS(x, y, c));
25 | }
26 | }
27 |
28 | private override function set_icon(value:String):String {
29 | if (_icon == value) {
30 | return value;
31 | }
32 | _icon = value;
33 |
34 | ToolkitAssets.instance.getImage(_icon, function(imageInfo) {
35 | if (imageInfo != null) {
36 | var iconImage = Image.fromBitmapData(imageInfo.data.parent.bitmap);
37 | Lib.current.stage.window.setIcon(iconImage);
38 | }
39 | });
40 |
41 | return value;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/haxe/ui/backend/AssetsImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import flixel.graphics.FlxGraphic;
4 | import flixel.graphics.frames.FlxFrame;
5 | import flixel.graphics.frames.FlxImageFrame;
6 | import haxe.io.Bytes;
7 | import haxe.ui.assets.FontInfo;
8 | import haxe.ui.assets.ImageInfo;
9 | import haxe.ui.backend.ImageData;
10 | import openfl.Assets;
11 | import openfl.display.Bitmap;
12 | import openfl.display.Loader;
13 | import openfl.events.Event;
14 | import openfl.utils.AssetType;
15 | import openfl.utils.ByteArray;
16 |
17 | class AssetsImpl extends AssetsBase {
18 | private override function getImageInternal(resourceId:String, callback:ImageInfo->Void):Void {
19 | var graphic:FlxGraphic = null;
20 | var frame:FlxFrame = null;
21 |
22 | if (Assets.exists(resourceId)) {
23 | graphic = FlxGraphic.fromAssetKey(resourceId);
24 | frame = FlxImageFrame.fromGraphic(graphic).frame;
25 | }
26 |
27 | if (frame != null) {
28 | frame.parent.persist = true;
29 | frame.parent.destroyOnNoUse = false;
30 | callback({
31 | data : frame,
32 | width : Std.int(frame.sourceSize.x),
33 | height : Std.int(frame.sourceSize.y)
34 | });
35 | } else {
36 | callback(null);
37 | }
38 | }
39 |
40 | private override function getImageFromHaxeResource(resourceId:String, callback:String->ImageInfo->Void):Void {
41 | if (Resource.listNames().indexOf(resourceId) == -1) {
42 | callback(resourceId, null);
43 | } else {
44 | var bytes = Resource.getBytes(resourceId);
45 | imageFromBytes(bytes, callback.bind(resourceId));
46 | }
47 | }
48 |
49 | public override function imageFromBytes(bytes:Bytes, callback:ImageInfo->Void):Void {
50 | var ba:ByteArray = ByteArray.fromBytes(bytes);
51 | var loader:Loader = new Loader();
52 | loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e) {
53 | if (loader.content != null) {
54 | var frame = FlxImageFrame.fromImage(cast(loader.content, Bitmap).bitmapData).frame;
55 | frame.parent.persist = true; // these two booleans will screw up the UI unless changed from the default values
56 | frame.parent.destroyOnNoUse = false;
57 | callback({
58 | data : frame,
59 | width : Std.int(frame.sourceSize.x),
60 | height : Std.int(frame.sourceSize.y)
61 | });
62 | } else {
63 | callback(null);
64 | }
65 | });
66 | loader.contentLoaderInfo.addEventListener("ioError", function(e) {
67 | trace(e);
68 | callback(null);
69 | });
70 |
71 | loader.loadBytes(ba);
72 | }
73 |
74 | private override function getFontInternal(resourceId:String, callback:FontInfo->Void):Void {
75 | var fontName:String = null;
76 | if (isEmbeddedFont(resourceId) && Assets.exists(resourceId, AssetType.FONT)) {
77 | fontName = Assets.getFont(resourceId).fontName;
78 | } else {
79 | fontName = resourceId;
80 | }
81 | callback({
82 | data : fontName
83 | });
84 | }
85 |
86 | private override function getTextDelegate(resourceId:String):String {
87 | if (Assets.exists(resourceId)) {
88 | return Assets.getText(resourceId);
89 | }
90 | return null;
91 | }
92 |
93 | public override function imageInfoFromImageData(imageData:ImageData):ImageInfo {
94 | return {
95 | data: imageData,
96 | width: Std.int(imageData.frame.width),
97 | height: Std.int(imageData.frame.height)
98 | }
99 | }
100 |
101 | private static inline function isEmbeddedFont(fontName:String):Bool {
102 | return fontName != "_sans" && fontName != "_serif" && fontName != "_typewriter";
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/haxe/ui/backend/BackendImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | class BackendImpl {
4 | public static var id:String = "flixel";
5 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/CallLaterImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import flixel.FlxG;
4 |
5 | // we'll flip between two list references between updates meaning there is breathing space
6 | // between calls rather that just filling a list and blocking
7 | class CallLaterImpl {
8 |
9 | private static var added:Bool = false;
10 | private static var current:ArrayVoid>;
11 | private static var list1:ArrayVoid> = [];
12 | private static var list2:ArrayVoid> = [];
13 | public function new(fn:Void->Void) {
14 | if (!added) {
15 | added = true;
16 | current = list1;
17 | FlxG.signals.preUpdate.add(onUpdate);
18 | }
19 | current.insert(0, fn);
20 | }
21 |
22 | private static function onUpdate() {
23 | var ref = current;
24 | if (current == list1) {
25 | current = list2;
26 | } else {
27 | current = list1;
28 | }
29 | while (ref.length > 0) {
30 | ref.pop()();
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/haxe/ui/backend/ComponentGraphicsImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import flixel.FlxSprite;
4 | import haxe.io.Bytes;
5 | import haxe.ui.core.Component;
6 | import haxe.ui.loaders.image.ImageLoader;
7 | import haxe.ui.util.Color;
8 | import haxe.ui.util.Variant;
9 | import openfl.display.BitmapData;
10 | import openfl.display.GraphicsPath;
11 | import openfl.display.GraphicsPathCommand;
12 | import openfl.display.Sprite;
13 | import openfl.geom.Matrix;
14 | import openfl.geom.Rectangle;
15 | import openfl.utils.ByteArray;
16 |
17 | @:allow(haxe.ui.backend.ComponentGraphicsSprite)
18 | class ComponentGraphicsImpl extends ComponentGraphicsBase {
19 | private var _hasSize:Bool = false;
20 | private var bitmapData:BitmapData = null;
21 | private var sprite:ComponentGraphicsSprite;
22 |
23 | private var flashGfxSprite:Sprite = new Sprite();
24 |
25 | private var _currentFillColor:Null = null;
26 | private var _currentFillAlpha:Null = null;
27 | private var _globalFillColor:Null = null;
28 | private var _globalFillAlpha:Null = null;
29 |
30 | private var _globalLineThickness:Null = null;
31 | private var _globalLineColor:Null = null;
32 | private var _globalLineAlpha:Null = null;
33 |
34 | private var currentPath:GraphicsPath;
35 |
36 | public function new(component:Component) {
37 | super(component);
38 | sprite = new ComponentGraphicsSprite(this);
39 | sprite.active = false;
40 | sprite.visible = false;
41 | _component.add(sprite);
42 | }
43 |
44 | public override function clear() {
45 | super.clear();
46 | if (_hasSize == false) {
47 | return;
48 | }
49 | flashGfxSprite.graphics.clear();
50 |
51 | sprite.pixels.fillRect(sprite.pixels.rect, 0x00000000);
52 | sprite._needsDraw = true;
53 | }
54 |
55 | public override function setPixel(x:Float, y:Float, color:Color) {
56 | super.setPixel(x, y, color);
57 | if (_hasSize == false) {
58 | return;
59 | }
60 | flashGfxSprite.graphics.beginFill(color);
61 | flashGfxSprite.graphics.drawRect(x, y, 1, 1);
62 | flashGfxSprite.graphics.endFill();
63 | sprite._needsDraw = true;
64 | }
65 |
66 | public override function setPixels(pixels:Bytes) {
67 | super.setPixels(pixels);
68 | if (_hasSize == false) {
69 | return;
70 | }
71 |
72 | var w = Std.int(_component.width);
73 | var h = Std.int(_component.height);
74 |
75 | if (bitmapData != null && (bitmapData.width != w || bitmapData.height != h)) {
76 | bitmapData.dispose();
77 | bitmapData = null;
78 | }
79 |
80 | if (bitmapData == null) {
81 | bitmapData = new BitmapData(w, h, true, 0x00000000);
82 | }
83 |
84 | // convert RGBA -> ARGB (well, actually BGRA for some reason)
85 | var bytesData = pixels.getData();
86 | var length:Int = pixels.length;
87 | var newPixels = Bytes.alloc(length);
88 | var i:Int = 0;
89 | while (i < length) {
90 | var r = Bytes.fastGet(bytesData, i + 0);
91 | var g = Bytes.fastGet(bytesData, i + 1);
92 | var b = Bytes.fastGet(bytesData, i + 2);
93 | var a = Bytes.fastGet(bytesData, i + 3);
94 | newPixels.set(i + 0, b);
95 | newPixels.set(i + 1, g);
96 | newPixels.set(i + 2, r);
97 | newPixels.set(i + 3, a);
98 | i += 4;
99 | }
100 | var byteArray = ByteArray.fromBytes(newPixels);
101 | bitmapData.setPixels(new Rectangle(0, 0, bitmapData.width, bitmapData.height), byteArray);
102 |
103 | sprite.width = w;
104 | sprite.height = h;
105 |
106 | sprite.pixels = bitmapData;
107 | sprite.visible = (w > 0 && h > 0);
108 | }
109 |
110 | public override function moveTo(x:Float, y:Float) {
111 | super.moveTo(x, y);
112 | if (_hasSize == false) {
113 | return;
114 | }
115 | if (currentPath != null) {
116 | currentPath.moveTo(x, y);
117 | } else {
118 | flashGfxSprite.graphics.moveTo(x, y);
119 | sprite._needsDraw = true;
120 | }
121 | }
122 |
123 | public override function lineTo(x:Float, y:Float) {
124 | super.lineTo(x, y);
125 | if (_hasSize == false) {
126 | return;
127 | }
128 | if (currentPath != null) {
129 | currentPath.lineTo(x, y);
130 | } else {
131 | flashGfxSprite.graphics.lineTo(x, y);
132 | sprite._needsDraw = true;
133 | }
134 | }
135 |
136 | public override function strokeStyle(color:Null, thickness:Null = 1, alpha:Null = 1) {
137 | super.strokeStyle(color, thickness, alpha);
138 | if (_hasSize == false) {
139 | return;
140 | }
141 | if (currentPath == null) {
142 | _globalLineThickness = thickness;
143 | _globalLineColor = color;
144 | _globalLineAlpha = alpha;
145 | }
146 |
147 | flashGfxSprite.graphics.lineStyle(thickness, color, alpha);
148 | }
149 |
150 | public override function circle(x:Float, y:Float, radius:Float) {
151 | super.circle(x, y, radius);
152 | if (_hasSize == false) {
153 | return;
154 | }
155 | if (_currentFillColor != null) {
156 | flashGfxSprite.graphics.beginFill(_currentFillColor, _currentFillAlpha);
157 | }
158 | flashGfxSprite.graphics.drawCircle(x, y, radius);
159 | if (_currentFillColor != null) {
160 | flashGfxSprite.graphics.endFill();
161 | }
162 | sprite._needsDraw = true;
163 | }
164 |
165 | public override function fillStyle(color:Null, alpha:Null = 1) {
166 | super.fillStyle(color, alpha);
167 | if (_hasSize == false) {
168 | return;
169 | }
170 | if (currentPath == null) {
171 | _globalFillColor = color;
172 | _globalFillAlpha = alpha;
173 | }
174 | _currentFillColor = color;
175 | _currentFillAlpha = alpha;
176 | }
177 |
178 | public override function curveTo(controlX:Float, controlY:Float, anchorX:Float, anchorY:Float) {
179 | super.curveTo(controlX, controlY, anchorX, anchorY);
180 | if (_hasSize == false) {
181 | return;
182 | }
183 |
184 | if (currentPath != null) {
185 | currentPath.curveTo(controlX, controlY, anchorX, anchorY);
186 | } else {
187 | flashGfxSprite.graphics.curveTo(controlX, controlY, anchorX, anchorY);
188 | sprite._needsDraw = true;
189 | }
190 | }
191 |
192 | public override function cubicCurveTo(controlX1:Float, controlY1:Float, controlX2:Float, controlY2:Float, anchorX:Float, anchorY:Float) {
193 | super.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY);
194 | if (_hasSize == false) {
195 | return;
196 | }
197 | if (currentPath != null) {
198 | currentPath.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY);
199 | } else {
200 | flashGfxSprite.graphics.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY);
201 | sprite._needsDraw = true;
202 | }
203 | }
204 |
205 | public override function rectangle(x:Float, y:Float, width:Float, height:Float) {
206 | super.rectangle(x, y, width, height);
207 | if (_hasSize == false) {
208 | return;
209 | }
210 | if (_currentFillColor != null) {
211 | flashGfxSprite.graphics.beginFill(_currentFillColor, _currentFillAlpha);
212 | }
213 | flashGfxSprite.graphics.drawRect(x, y, width, height);
214 | if (_currentFillColor != null) {
215 | flashGfxSprite.graphics.endFill();
216 | }
217 | sprite._needsDraw = true;
218 | }
219 |
220 | public override function image(resource:Variant, x:Null = null, y:Null = null, width:Null = null, height:Null = null) {
221 | super.image(resource, x, y, width, height);
222 | if (_hasSize == false) {
223 | return;
224 | }
225 | ImageLoader.instance.load(resource, function(imageInfo) {
226 | if (imageInfo != null) {
227 | if (x == null) x = 0;
228 | if (y == null) y = 0;
229 | if (width == null) width = imageInfo.width;
230 | if (height == null) height = imageInfo.height;
231 |
232 | var mat:Matrix = new Matrix();
233 | mat.scale(width / imageInfo.width, height / imageInfo.width);
234 | mat.translate(x, y);
235 |
236 | flashGfxSprite.graphics.beginBitmapFill(imageInfo.data.parent.bitmap, mat);
237 | flashGfxSprite.graphics.drawRect(x, y, width, height);
238 | flashGfxSprite.graphics.endFill();
239 | sprite._needsDraw = true;
240 | } else {
241 | trace("could not load: " + resource);
242 | }
243 | });
244 | }
245 |
246 | public override function beginPath() {
247 | super.beginPath();
248 | if (_hasSize == false) {
249 | return;
250 | }
251 | currentPath = new GraphicsPath();
252 | }
253 |
254 | public override function closePath() {
255 | super.closePath();
256 | if (_hasSize == false) {
257 | return;
258 | }
259 | if (currentPath != null && currentPath.commands != null && currentPath.commands.length > 0) {
260 | if (_currentFillColor != null) {
261 | flashGfxSprite.graphics.beginFill(_currentFillColor, _currentFillAlpha);
262 | }
263 | if (currentPath.commands[0] != GraphicsPathCommand.MOVE_TO) {
264 | currentPath.commands.insertAt(0, GraphicsPathCommand.MOVE_TO);
265 | #if !flash
266 | @:privateAccess currentPath.data.insertAt(0, flashGfxSprite.graphics.__positionX);
267 | @:privateAccess currentPath.data.insertAt(0, flashGfxSprite.graphics.__positionY);
268 | #end
269 | }
270 | flashGfxSprite.graphics.drawPath(currentPath.commands, currentPath.data);
271 | if (_currentFillColor != null) {
272 | flashGfxSprite.graphics.endFill();
273 | }
274 | sprite._needsDraw = true;
275 | }
276 | currentPath = null;
277 | _currentFillColor = _globalFillColor;
278 | _currentFillAlpha = _globalFillAlpha;
279 |
280 | // it seems openfl forgets about lineStyle after drawing a shape;
281 | flashGfxSprite.graphics.lineStyle(_globalLineThickness, _globalLineColor, _globalLineAlpha);
282 | }
283 |
284 | public override function resize(width:Null, height:Null) {
285 | if (width > 0 && height > 0) {
286 | if (_hasSize == false) {
287 | _hasSize = true;
288 | sprite.makeGraphic(Std.int(width), Std.int(height), 0x00000000, true);
289 | sprite.visible = true;
290 | replayDrawCommands();
291 | }
292 | }
293 | }
294 | }
295 |
296 | @:allow(haxe.ui.backend.ComponentGraphicsImpl)
297 | class ComponentGraphicsSprite extends FlxSprite {
298 | private var componentGraphics:ComponentGraphicsImpl;
299 |
300 | private var _needsDraw:Bool = false;
301 |
302 | public function new(componentGraphics:ComponentGraphicsImpl) {
303 | super();
304 | this.componentGraphics = componentGraphics;
305 | }
306 |
307 | public override function draw() {
308 | if (pixels != null && _needsDraw) {
309 | pixels.draw(componentGraphics.flashGfxSprite);
310 | _needsDraw = false;
311 | }
312 | super.draw();
313 | }
314 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/ComponentImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import flixel.FlxG;
4 | import flixel.FlxSprite;
5 | import flixel.FlxState;
6 | import flixel.math.FlxRect;
7 | import flixel.text.FlxText.FlxTextBorderStyle;
8 | import haxe.ui.Toolkit;
9 | import haxe.ui.backend.TextInputImpl.TextInputEvent;
10 | import haxe.ui.backend.flixel.FlxStyleHelper;
11 | import haxe.ui.core.Component;
12 | import haxe.ui.core.ImageDisplay;
13 | import haxe.ui.core.Platform;
14 | import haxe.ui.core.Screen;
15 | import haxe.ui.core.TextDisplay;
16 | import haxe.ui.core.TextInput;
17 | import haxe.ui.events.KeyboardEvent;
18 | import haxe.ui.events.MouseEvent;
19 | import haxe.ui.events.UIEvent;
20 | import haxe.ui.filters.DropShadow;
21 | import haxe.ui.filters.Outline;
22 | import haxe.ui.geom.Rectangle;
23 | import haxe.ui.styles.Style;
24 |
25 | class ComponentImpl extends ComponentBase {
26 | private var _eventMap:MapVoid>;
27 |
28 | private var _surface:FlxSprite;
29 |
30 | private var asComponent:Component;
31 |
32 | public function new() {
33 | super();
34 | _eventMap = new MapVoid>();
35 |
36 | this.pixelPerfectRender = true;
37 | this.moves = false;
38 | superVisible(false);
39 |
40 | asComponent = cast(this, Component);
41 |
42 | if (Platform.instance.isMobile) {
43 | asComponent.addClass(":mobile");
44 | }
45 |
46 | scrollFactor.set(0, 0); // ui doesn't scroll by default
47 |
48 | _surface = new FlxSprite();
49 | _surface.pixelPerfectRender = true;
50 | _surface.active = false;
51 | _surface.visible = false;
52 | add(_surface);
53 |
54 | //recursiveReady();
55 | }
56 |
57 | private function recursiveReady() {
58 | asComponent.ready();
59 | for (child in asComponent.childComponents) {
60 | child.recursiveReady();
61 | }
62 | }
63 |
64 | private var _state:FlxState;
65 | public var state(get, set):FlxState;
66 | private function get_state():FlxState {
67 | return findRootComponent()._state;
68 | }
69 | private function set_state(value:FlxState):FlxState {
70 | findRootComponent()._state = value;
71 | return value;
72 | }
73 |
74 |
75 | // lets cache certain items so we dont have to loop multiple times per frame
76 | private var _cachedScreenX:Null = null;
77 | private var _cachedScreenY:Null = null;
78 | private var _cachedClipComponent:Component = null;
79 | private var _cachedClipComponentNone:Null = null;
80 | private var _cachedRootComponent:Component = null;
81 |
82 | private function clearCaches() {
83 | _cachedScreenX = null;
84 | _cachedScreenY = null;
85 | _cachedClipComponent = null;
86 | _cachedClipComponentNone = null;
87 | _cachedRootComponent = null;
88 | }
89 |
90 | private function cacheScreenPos() {
91 | if (_cachedScreenX != null && _cachedScreenY != null) {
92 | return;
93 | }
94 | var screenBounds = asComponent.screenBounds;
95 | _cachedScreenX = screenBounds.left;
96 | _cachedScreenY = screenBounds.top;
97 | }
98 |
99 | private var cachedScreenX(get, null):Float;
100 | private function get_cachedScreenX():Float {
101 | cacheScreenPos();
102 | return _cachedScreenX;
103 | }
104 |
105 | private var cachedScreenY(get, null):Float;
106 | private function get_cachedScreenY():Float {
107 | cacheScreenPos();
108 | return _cachedScreenY;
109 | }
110 |
111 | private function findRootComponent():Component {
112 | if (_cachedRootComponent != null) {
113 | return _cachedRootComponent;
114 | }
115 |
116 | var c:Component = asComponent;
117 | while (c.parentComponent != null) {
118 | c = c.parentComponent;
119 | }
120 |
121 | _cachedRootComponent = c;
122 |
123 | return c;
124 | }
125 |
126 | private function isRootComponent():Bool {
127 | return (findRootComponent() == this);
128 | }
129 |
130 | private function findClipComponent():Component {
131 | if (_cachedClipComponent != null) {
132 | return _cachedClipComponent;
133 | } else if (_cachedClipComponentNone == true) {
134 | return null;
135 | }
136 |
137 | var c:Component = asComponent;
138 | var clip:Component = null;
139 | while (c != null) {
140 | if (c.componentClipRect != null) {
141 | clip = c;
142 | break;
143 | }
144 | c = c.parentComponent;
145 | }
146 |
147 | _cachedClipComponent = clip;
148 | if (clip == null) {
149 | _cachedClipComponentNone = true;
150 | }
151 |
152 | return clip;
153 | }
154 |
155 | @:access(haxe.ui.core.Component)
156 | private function inBounds(x:Float, y:Float):Bool {
157 | if (asComponent.hidden == true) {
158 | return false;
159 | }
160 |
161 | var b:Bool = false;
162 | var sx = cachedScreenX;
163 | var sy = cachedScreenY;
164 | var cx = asComponent.componentWidth * Toolkit.scaleX;
165 | var cy = asComponent.componentHeight * Toolkit.scaleY;
166 |
167 | if (x >= sx && y >= sy && x <= sx + cx && y < sy + cy) {
168 | b = true;
169 | }
170 |
171 | // let make sure its in the clip rect too
172 | if (b == true) {
173 | var clip:Component = findClipComponent();
174 | if (clip != null) {
175 | b = false;
176 | var sx = (clip.cachedScreenX + (clip.componentClipRect.left * Toolkit.scaleX));
177 | var sy = (clip.cachedScreenY + (clip.componentClipRect.top * Toolkit.scaleY));
178 | var cx = clip.componentClipRect.width * Toolkit.scaleX;
179 | var cy = clip.componentClipRect.height * Toolkit.scaleY;
180 | if (x >= sx && y >= sy && x <= sx + cx && y < sy + cy) {
181 | b = true;
182 | }
183 | }
184 | }
185 | return b;
186 | }
187 |
188 | private override function handlePosition(left:Null, top:Null, style:Style) {
189 | if (left == null && top == null) {
190 | return;
191 | }
192 |
193 | if (parentComponent == null) {
194 | if (left != null) {
195 | //this.x = left;
196 | }
197 | if (top != null) {
198 | //this.y = top;
199 | }
200 | }
201 | }
202 |
203 | private override function handleSize(width:Null, height:Null, style:Style) {
204 | if (_surface == null) {
205 | return;
206 | }
207 |
208 | if (width == null || height == null) {
209 | return;
210 | }
211 |
212 | var w:Int = Std.int(width * Toolkit.scaleX);
213 | var h:Int = Std.int(height * Toolkit.scaleY);
214 | if (_surface.width != w || _surface.height != h) {
215 | if (w <= 0 || h <= 0) {
216 | _surface.makeGraphic(1, 1, 0x0, true);
217 | _surface.visible = false;
218 | } else {
219 | _surface.makeGraphic(w, h, 0x0, true);
220 | applyStyle(style);
221 | }
222 | }
223 | }
224 |
225 | //***********************************************************************************************************
226 | // Display tree
227 | //***********************************************************************************************************
228 |
229 | private override function handleDestroy() {
230 | destroyInternal();
231 | }
232 |
233 | private override function handleSetComponentIndex(child:Component, index:Int) {
234 | handleAddComponentAt(child, index);
235 | }
236 |
237 | private override function handleAddComponent(child:Component):Component {
238 | handleAddComponentAt(child, childComponents.length - 1);
239 | return child;
240 | }
241 |
242 | private function superVisible(value:Bool) {
243 | _skipTransformChildren = true;
244 | super.set_visible(value);
245 | _skipTransformChildren = false;
246 | }
247 |
248 | private override function handleAddComponentAt(child:Component, index:Int):Component {
249 | // index is in terms of haxeui components, not flixel children
250 | var indexOffset = 0;
251 | while (indexOffset < members.length) {
252 | if (!(members[indexOffset] is Component)) {
253 | indexOffset++;
254 | } else{
255 | break;
256 | }
257 | }
258 |
259 | insert(index + indexOffset, child);
260 | return child;
261 | }
262 |
263 | private var _unsolicitedMembers:Array<{sprite: FlxSprite, originalX:Float, originalY:Float}> = null;
264 | private override function preAdd(sprite:FlxSprite) {
265 | if (isUnsolicitedMember(sprite)) {
266 | if (_unsolicitedMembers == null) {
267 | _unsolicitedMembers = [];
268 | }
269 | if (findUnsolictedEntryFromSprite(sprite) == null) {
270 | var use = true;
271 | if (_textInput != null && _textInput.equals(sprite)) {
272 | use = false;
273 | }
274 | if (use) {
275 | _unsolicitedMembers.push({
276 | sprite: sprite,
277 | originalX: sprite.x,
278 | originalY: sprite.y
279 | });
280 | }
281 | }
282 | }
283 | super.preAdd(sprite);
284 | }
285 |
286 | public override function remove(sprite:FlxSprite, splice:Bool = false):FlxSprite {
287 | if (isUnsolicitedMember(sprite) && _unsolicitedMembers != null) {
288 | var um = findUnsolictedEntryFromSprite(sprite);
289 | _unsolicitedMembers.remove(um);
290 | }
291 | return super.remove(sprite, splice);
292 | }
293 |
294 | private function findUnsolictedEntryFromSprite(sprite:FlxSprite):{sprite: FlxSprite, originalX:Float, originalY:Float} {
295 | if (_unsolicitedMembers == null) {
296 | return null;
297 | }
298 | for (um in _unsolicitedMembers) {
299 | if (um.sprite == sprite) {
300 | return um;
301 | }
302 | }
303 | return null;
304 | }
305 |
306 | private var _destroy:Bool = false;
307 | private override function handleRemoveComponent(child:Component, dispose:Bool = true):Component {
308 | if (this.exists == false) { // lets make sure this component exists - it could have been destroyed through a variety of different ways already (like switching state for example, or simply manually destroying it)
309 | return child;
310 | }
311 | if (members.indexOf(child) > -1) {
312 | remove(child, true);
313 | if (dispose == true) {
314 | child._destroy = true;
315 | child.destroyInternal();
316 | }
317 | }
318 | return child;
319 | }
320 |
321 | private override function handleRemoveComponentAt(index:Int, dispose:Bool = true):Component {
322 | return handleRemoveComponent(this.childComponents[index], dispose);
323 | }
324 |
325 | private override function handleClipRect(value:Rectangle):Void {
326 | if (value == null) {
327 | clipRect = null;
328 | }
329 | }
330 |
331 | private override function handleVisibility(show:Bool):Void {
332 | applyVisibility(show);
333 | }
334 |
335 | private function applyVisibility(show:Bool):Void {
336 | superVisible(show);
337 |
338 | if (hasTextDisplay()) {
339 | _textDisplay.tf.visible = show;
340 | }
341 | if (hasTextInput()) {
342 | _textInput.visible = show;
343 | }
344 |
345 | for (c in this.childComponents) {
346 | c.applyVisibility(show && !c.hidden);
347 | }
348 | }
349 |
350 | //***********************************************************************************************************
351 | // Style
352 | //***********************************************************************************************************
353 | private override function applyStyle(style:Style) {
354 | if (style.opacity != null) {
355 | applyAlpha(style.opacity);
356 | } else if (_surface.alpha != 1) {
357 | //applyAlpha(1);
358 | }
359 |
360 | /*
361 | if (style != null && style.cursor != null && _mouseOverFlag) {
362 | Screen.instance.setCursor(this.style.cursor, this.style.cursorOffsetX, this.style.cursorOffsetY);
363 | _cursorSet = true;
364 | }
365 | */
366 |
367 | _surface.visible = FlxStyleHelper.applyStyle(_surface, style);
368 | applyFilters(style);
369 | }
370 |
371 | private function applyAlpha(value:Float) {
372 | _surface.alpha = value;
373 | if (hasTextDisplay()) {
374 | getTextDisplay().tf.alpha = value;
375 | }
376 | if (hasTextInput()) {
377 | getTextInput().alpha = value;
378 | }
379 | if (hasImageDisplay()) {
380 | getImageDisplay().alpha = value;
381 | }
382 | for (c in childComponents) {
383 | if (c.style != null && c.style.opacity != null) {
384 | c.applyAlpha(c.style.opacity * value);
385 | } else {
386 | c.applyAlpha(value);
387 | }
388 | }
389 | }
390 |
391 | public override function set_alpha(alpha:Float):Float {
392 | _surface.alpha = alpha;
393 | if (hasTextDisplay()) {
394 | getTextDisplay().tf.alpha = alpha;
395 | }
396 | if (hasTextInput()) {
397 | getTextInput().alpha = alpha;
398 | }
399 | if (hasImageDisplay()) {
400 | getImageDisplay().alpha = alpha;
401 | }
402 | return super.set_alpha(alpha);
403 | }
404 |
405 | private function applyFilters(style:Style) {
406 | if (style.filter != null && style.filter.length > 0) {
407 | for (f in style.filter) {
408 | if (_textDisplay != null && (f is Outline)) {
409 | var o = cast(f, Outline);
410 | var col = o.color;
411 | _textDisplay.tf.setBorderStyle(FlxTextBorderStyle.OUTLINE, 0xFF000000 | o.color, o.size);
412 | } else if (_textDisplay != null && (f is DropShadow)) {
413 | var o = cast(f, DropShadow);
414 | _textDisplay.tf.setBorderStyle(FlxTextBorderStyle.SHADOW, 0xFF000000 | o.color, o.distance);
415 | }
416 | }
417 | }
418 | }
419 |
420 | //***********************************************************************************************************
421 | // Image
422 | //***********************************************************************************************************
423 | public override function createImageDisplay():ImageDisplay {
424 | if (_imageDisplay == null) {
425 | super.createImageDisplay();
426 | _imageDisplay.visible = false;
427 | add(_imageDisplay);
428 | Toolkit.callLater(function() { // lets show it a frame later so its had a chance to reposition
429 | if (_imageDisplay != null) {
430 | _imageDisplay.visible = true;
431 | }
432 | });
433 | }
434 |
435 | return _imageDisplay;
436 | }
437 |
438 | public override function removeImageDisplay():Void {
439 | if (_imageDisplay != null) {
440 | remove(_imageDisplay, true);
441 | _imageDisplay.destroy();
442 | _imageDisplay = null;
443 | }
444 | }
445 |
446 | //***********************************************************************************************************
447 | // Events
448 | //***********************************************************************************************************
449 | private override function mapEvent(type:String, listener:UIEvent->Void) {
450 | switch (type) {
451 | case MouseEvent.MOUSE_DOWN:
452 | if (_eventMap.exists(MouseEvent.MOUSE_DOWN) == false) {
453 | if (hasTextInput()) {
454 | _eventMap.set(MouseEvent.MOUSE_DOWN, listener);
455 | getTextInput().onMouseDown = __onTextInputMouseEvent;
456 | }
457 | }
458 |
459 | case MouseEvent.MOUSE_UP:
460 | if (_eventMap.exists(MouseEvent.MOUSE_UP) == false) {
461 | if (hasTextInput()) {
462 | _eventMap.set(MouseEvent.MOUSE_UP, listener);
463 | getTextInput().onMouseUp = __onTextInputMouseEvent;
464 | }
465 | }
466 |
467 | case MouseEvent.CLICK:
468 | if (_eventMap.exists(MouseEvent.CLICK) == false) {
469 | if (hasTextInput()) {
470 | _eventMap.set(MouseEvent.CLICK, listener);
471 | getTextInput().onClick = __onTextInputMouseEvent;
472 | }
473 | }
474 |
475 | case KeyboardEvent.KEY_DOWN:
476 | if (_eventMap.exists(KeyboardEvent.KEY_DOWN) == false) {
477 | if (hasTextInput()) {
478 | _eventMap.set(KeyboardEvent.KEY_DOWN, listener);
479 | getTextInput().onKeyDown = __onTextInputKeyboardEvent;
480 | }
481 | }
482 |
483 | case KeyboardEvent.KEY_UP:
484 | if (_eventMap.exists(KeyboardEvent.KEY_UP) == false) {
485 | if (hasTextInput()) {
486 | _eventMap.set(KeyboardEvent.KEY_UP, listener);
487 | getTextInput().onKeyUp = __onTextInputKeyboardEvent;
488 | }
489 | }
490 |
491 | case UIEvent.CHANGE:
492 | if (_eventMap.exists(UIEvent.CHANGE) == false) {
493 | if (hasTextInput() == true) {
494 | _eventMap.set(UIEvent.CHANGE, listener);
495 | getTextInput().onChange = __onTextInputChange;
496 | }
497 | }
498 | }
499 | }
500 |
501 | private override function unmapEvent(type:String, listener:UIEvent->Void) {
502 | switch (type) {
503 | case MouseEvent.MOUSE_DOWN:
504 | if (hasTextInput()) {
505 | _eventMap.remove(type);
506 | getTextInput().onMouseDown = null;
507 | }
508 |
509 | case MouseEvent.MOUSE_UP:
510 | if (hasTextInput()) {
511 | _eventMap.remove(type);
512 | getTextInput().onMouseUp = null;
513 | }
514 |
515 | case MouseEvent.CLICK:
516 | if (hasTextInput()) {
517 | _eventMap.remove(type);
518 | getTextInput().onClick = null;
519 | }
520 |
521 | case KeyboardEvent.KEY_DOWN:
522 | if (hasTextInput()) {
523 | _eventMap.remove(type);
524 | getTextInput().onKeyDown = null;
525 | }
526 |
527 | case KeyboardEvent.KEY_UP:
528 | if (hasTextInput()) {
529 | _eventMap.remove(type);
530 | getTextInput().onKeyUp = null;
531 | }
532 |
533 | case UIEvent.CHANGE:
534 | if (hasTextInput()) {
535 | _eventMap.remove(type);
536 | getTextInput().onChange = null;
537 | }
538 | }
539 | }
540 |
541 | private function __onTextInputChange(event:TextInputEvent) {
542 | var fn:UIEvent->Void = _eventMap.get(UIEvent.CHANGE);
543 | if (fn != null) {
544 | fn(new UIEvent(UIEvent.CHANGE));
545 | }
546 | }
547 |
548 | private function __onTextInputMouseEvent(event:TextInputEvent) {
549 | var type = null;
550 | switch (event.type) {
551 | case openfl.events.MouseEvent.MOUSE_DOWN:
552 | type = MouseEvent.MOUSE_DOWN;
553 | case openfl.events.MouseEvent.MOUSE_UP:
554 | type = MouseEvent.MOUSE_UP;
555 | case openfl.events.MouseEvent.CLICK:
556 | type = MouseEvent.CLICK;
557 | }
558 | var fn:UIEvent->Void = _eventMap.get(type);
559 | if (fn != null) {
560 | var mouseEvent = new haxe.ui.events.MouseEvent(type);
561 | mouseEvent.screenX = event.stageX / Toolkit.scaleX;
562 | mouseEvent.screenY = event.stageY / Toolkit.scaleY;
563 | if (Platform.instance.isMobile) {
564 | mouseEvent.touchEvent = true;
565 | }
566 | fn(mouseEvent);
567 | }
568 | }
569 |
570 | /*
571 | private var _mouseOverFlag:Bool = false;
572 | private var _cursorSet:Bool = false;
573 | private function __onMouseOver(event:MouseEvent) {
574 | _mouseOverFlag = true;
575 | }
576 |
577 | private function __onMouseOut(event:MouseEvent) {
578 | _mouseOverFlag = false;
579 | }
580 | */
581 |
582 | #if haxeui_dont_impose_base_class
583 | private function applyRootLayout(l:String) {
584 | }
585 | #end
586 |
587 | private function __onTextInputKeyboardEvent(event:openfl.events.KeyboardEvent) {
588 | var type = switch (event.type) {
589 | case openfl.events.KeyboardEvent.KEY_DOWN:
590 | KeyboardEvent.KEY_DOWN;
591 | case openfl.events.KeyboardEvent.KEY_UP:
592 | KeyboardEvent.KEY_UP;
593 | default:
594 | null;
595 | }
596 |
597 | var fn = _eventMap.get(type);
598 | if (fn == null) {
599 | return;
600 | }
601 |
602 | var keyboardEvent = new KeyboardEvent(type);
603 | keyboardEvent.keyCode = event.keyCode;
604 | keyboardEvent.altKey = event.altKey;
605 | keyboardEvent.ctrlKey = event.ctrlKey;
606 | keyboardEvent.shiftKey = event.shiftKey;
607 | fn(keyboardEvent);
608 | }
609 |
610 | //***********************************************************************************************************
611 | // Text related
612 | //***********************************************************************************************************
613 | public override function createTextDisplay(text:String = null):TextDisplay {
614 | if (_textDisplay == null) {
615 | super.createTextDisplay(text);
616 | _textDisplay.tf.visible = false;
617 | add(_textDisplay.tf);
618 | Toolkit.callLater(function() { // lets show it a frame later so its had a chance to reposition
619 | //_textDisplay.tf.visible = true;
620 | applyFilters(style);
621 | });
622 | }
623 |
624 | return _textDisplay;
625 | }
626 |
627 | public override function createTextInput(text:String = null):TextInput {
628 | if (_textInput == null) {
629 | super.createTextInput(text);
630 | _textInput.attach();
631 | _textInput.visible = false;
632 | _textInput.addToComponent(cast this);
633 | /*
634 | Toolkit.callLater(function() { // lets show it a frame later so its had a chance to reposition
635 | if (_textInput != null) {
636 | _textInput.tf.visible = true;
637 | }
638 | });
639 | */
640 | }
641 |
642 | return _textInput;
643 | }
644 | //***********************************************************************************************************
645 | // Util
646 | //***********************************************************************************************************
647 | private function repositionChildren() {
648 | var xpos = this.cachedScreenX;
649 | var ypos = this.cachedScreenY;
650 | if (_surface != null) {
651 | _surface.x = xpos;
652 | _surface.y = ypos;
653 | }
654 |
655 | if (_textDisplay != null) {
656 | var offsetX = 2 / Toolkit.scaleX;
657 | var offsetY = 2 / Toolkit.scaleY;
658 | _textDisplay.tf.x = xpos + _textDisplay.left - offsetX;
659 | _textDisplay.tf.y = ypos + _textDisplay.top - offsetY;
660 | }
661 |
662 | if (_textInput != null) {
663 | var offsetX = 2 / Toolkit.scaleX;
664 | var offsetY = 2 / Toolkit.scaleY;
665 | _textInput.x = (xpos + _textInput.left - offsetX);
666 | _textInput.y = (ypos + _textInput.top - offsetY);
667 | _textInput.scaleX = FlxG.scaleMode.scale.x;
668 | _textInput.scaleY = FlxG.scaleMode.scale.y;
669 | _textInput.update();
670 | }
671 |
672 | if (_imageDisplay != null) {
673 | var offsetX = 0;
674 | var offsetY = 0;
675 | _imageDisplay.x = xpos + _imageDisplay.left - offsetX;
676 | _imageDisplay.y = ypos + _imageDisplay.top - offsetY;
677 | }
678 |
679 | if (_unsolicitedMembers != null) {
680 | for (m in _unsolicitedMembers) {
681 | m.sprite.x = m.originalX + this.cachedScreenX;
682 | m.sprite.y = m.originalY + this.cachedScreenY;
683 | }
684 | }
685 | }
686 |
687 | private function isUnsolicitedMember(m:FlxSprite) {
688 | if (m == _surface) {
689 | return false;
690 | }
691 |
692 | if (_textDisplay != null && m == _textDisplay.tf) {
693 | return false;
694 | }
695 |
696 | if (m == _imageDisplay) {
697 | return false;
698 | }
699 |
700 | return !(m is Component);
701 | }
702 |
703 | private function hasComponentOver(ref:Component, x:Float, y:Float):Bool {
704 | var array:Array = getComponentsAtPoint(x, y);
705 | if (array.length == 0) {
706 | return false;
707 | }
708 |
709 | return !hasChildRecursive(cast ref, cast array[array.length - 1]);
710 | }
711 |
712 | private function getComponentsAtPoint(x:Float, y:Float, reverse:Bool = false):Array {
713 | var array:Array = new Array();
714 | for (r in Screen.instance.rootComponents) {
715 | findChildrenAtPoint(r, x, y, array);
716 | }
717 |
718 | if (reverse == true) {
719 | array.reverse();
720 | }
721 |
722 | return array;
723 | }
724 |
725 | private function findChildrenAtPoint(child:Component, x:Float, y:Float, array:Array) {
726 | if (child.inBounds(x, y) == true) {
727 | array.push(child);
728 | }
729 | for (c in child.childComponents) {
730 | findChildrenAtPoint(c, x, y, array);
731 | }
732 | }
733 |
734 | public function hasChildRecursive(parent:Component, child:Component):Bool {
735 | if (parent == child) {
736 | return true;
737 | }
738 | var r = false;
739 | for (t in parent.childComponents) {
740 | if (t == child) {
741 | r = true;
742 | break;
743 | }
744 |
745 | r = hasChildRecursive(t, child);
746 | if (r == true) {
747 | break;
748 | }
749 | }
750 |
751 | return r;
752 | }
753 |
754 | //***********************************************************************************************************
755 | // Flixel overrides
756 | //***********************************************************************************************************
757 |
758 | private var _updates:Float = 0;
759 | public override function update(elapsed:Float) {
760 | if (_destroyed) {
761 | super.update(elapsed);
762 | return;
763 | }
764 | if (_destroy == true) {
765 | clearCaches();
766 | destroyInternal();
767 | super.update(elapsed);
768 | return;
769 | }
770 |
771 | clearCaches();
772 | repositionChildren();
773 |
774 | _updates++;
775 | if (_updates == 2) {
776 | if (asComponent.hidden == false) {
777 | applyVisibility(true);
778 | } else {
779 | applyVisibility(false);
780 | }
781 | }
782 |
783 | super.update(elapsed);
784 |
785 | // only the root component will start the applyClipRect() chain
786 | if (parentComponent == null) {
787 | // this needs to be called after super.update() so the children's positions
788 | // get updated before clipping them
789 | applyClipRect();
790 | }
791 | }
792 |
793 | private function applyClipRect() {
794 | if (componentClipRect != null) {
795 | clipRect = getFlixelClipRect(clipRect);
796 | }
797 |
798 | for (c in childComponents) {
799 | c.applyClipRect();
800 | }
801 | }
802 |
803 | private function getFlixelClipRect(?rect:FlxRect):FlxRect {
804 | var value = componentClipRect;
805 | if (value == null) {
806 | return null;
807 | }
808 |
809 | value.top = Std.int(value.top);
810 | value.left = Std.int(value.left);
811 |
812 | if (rect == null) {
813 | rect = FlxRect.get();
814 | }
815 | rect.set((value.left * Toolkit.scaleX) + _surface.x - x,
816 | (value.top * Toolkit.scaleY) + _surface.y - y,
817 | (value.width * Toolkit.scaleX), (value.height * Toolkit.scaleY));
818 |
819 | // find the nearest parent with a clip rect so we can intersect it with
820 | // the one from this component
821 | var p = parentComponent;
822 | while (p != null) {
823 | if (p.componentClipRect != null) {
824 | var pRect = p.getFlixelClipRect();
825 | var oldRect = rect;
826 | rect = rect.intersection(pRect);
827 | pRect.put();
828 | oldRect.put();
829 | break;
830 | }
831 | p = p.parentComponent;
832 | }
833 |
834 | return rect;
835 | }
836 |
837 | // these functions (applyAddInternal / applyRemoveInternal) are called when a component is added / removed
838 | // from the screen - the main (only) reason this is important is because of textinputs:
839 | // textinputs are using openfl textfields, which means they are effectively floating over the top of the
840 | // application, this means that when things are removed from the screen (and not destroyed) it can leave them
841 | // behind
842 | private function applyAddInternal() {
843 | if (!TextInputImpl.USE_ON_ADDED) {
844 | return;
845 | }
846 |
847 | if (hasTextInput() && asComponent.hidden == false) {
848 | getTextInput().visible = true;
849 | }
850 | for (c in childComponents) {
851 | c.applyAddInternal();
852 | }
853 | }
854 |
855 | private function applyRemoveInternal() {
856 | if (!TextInputImpl.USE_ON_REMOVED) {
857 | return;
858 | }
859 |
860 | if (hasTextInput()) {
861 | getTextInput().visible = false;
862 | }
863 | for (c in childComponents) {
864 | c.applyRemoveInternal();
865 | }
866 | }
867 |
868 | private var _destroyed:Bool = false;
869 | private function destroyInternal() {
870 | if (_surface != null) {
871 | _surface.destroy();
872 | _surface = null;
873 | }
874 |
875 | if (_textDisplay != null) {
876 | _textDisplay.tf.destroy();
877 | _textDisplay = null;
878 | }
879 |
880 | if (_textInput != null) {
881 | _textInput.destroy(cast this);
882 | _textInput = null;
883 | }
884 |
885 | if (_imageDisplay != null) {
886 | _imageDisplay.destroy();
887 | _imageDisplay = null;
888 | }
889 |
890 | if (_unsolicitedMembers != null) {
891 | _unsolicitedMembers = null;
892 | }
893 |
894 | _state = null;
895 | _destroy = false;
896 | _destroyed = true;
897 | super.destroy();
898 | }
899 |
900 | public override function destroy():Void {
901 | if (parentComponent != null) {
902 | if (parentComponent.getComponentIndex(cast this) != -1) {
903 | parentComponent.removeComponent(cast this);
904 | return;
905 | }
906 | } else {
907 | Screen.instance.removeComponent(cast this);
908 | }
909 |
910 | _destroy = true;
911 | }
912 |
913 | private override function set_x(value:Float):Float {
914 | var r = super.set_x(value);
915 | if (this.parentComponent == null && _surface != null) {
916 | this.left = value;
917 | }
918 | return r;
919 | }
920 |
921 | private override function set_y(value:Float):Float {
922 | var r = super.set_y(value);
923 | if (this.parentComponent == null && _surface != null) {
924 | this.top = value;
925 | }
926 | return r;
927 | }
928 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/ComponentSurface.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | typedef ComponentSurface = flixel.group.FlxSpriteContainer;
--------------------------------------------------------------------------------
/haxe/ui/backend/EventImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | class EventImpl extends EventBase {
4 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/FontData.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | typedef FontData = String;
--------------------------------------------------------------------------------
/haxe/ui/backend/ImageData.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | //typedef ImageData = flixel.graphics.FlxGraphic;
4 | typedef ImageData = flixel.graphics.frames.FlxFrame;
--------------------------------------------------------------------------------
/haxe/ui/backend/ImageDisplayImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import flixel.graphics.frames.FlxImageFrame;
4 | import flixel.math.FlxRect;
5 | import haxe.ui.Toolkit;
6 |
7 | class ImageDisplayImpl extends ImageBase {
8 | public function new() {
9 | super();
10 | this.pixelPerfectRender = true;
11 | this.active = false;
12 | }
13 |
14 | private override function validateData():Void {
15 | if (_imageInfo != null) {
16 | frames = FlxImageFrame.fromFrame(_imageInfo.data);
17 |
18 | aspectRatio = _imageInfo.width / _imageInfo.height;
19 |
20 | origin.set(0, 0);
21 | }
22 | }
23 |
24 | private override function validateDisplay() {
25 | var scaleX:Float = _imageWidth / (_imageInfo.width / Toolkit.scaleX);
26 | var scaleY:Float = _imageHeight / (_imageInfo.height / Toolkit.scaleY);
27 | scale.set(scaleX, scaleY);
28 |
29 | width = Math.abs(scaleX) * frameWidth;
30 | height = Math.abs(scaleY) * frameHeight;
31 | }
32 |
33 | override function set_clipRect(rect:FlxRect):FlxRect {
34 | if (rect != null) {
35 | return super.set_clipRect(FlxRect.get(rect.x / scale.x, rect.y / scale.y, rect.width / scale.x, rect.height / scale.y));
36 | } else {
37 | return super.set_clipRect(null);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/haxe/ui/backend/ImageSurface.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | typedef ImageSurface = flixel.FlxSprite;
--------------------------------------------------------------------------------
/haxe/ui/backend/OpenFileDialogImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import haxe.ui.containers.dialogs.Dialogs.SelectedFileInfo;
4 |
5 | using StringTools;
6 | #if !js
7 | import haxe.io.Bytes;
8 | import haxe.ui.containers.dialogs.Dialog.DialogButton;
9 | import openfl.events.Event;
10 | import openfl.net.FileFilter;
11 | import openfl.net.FileReference;
12 | import openfl.net.FileReferenceList;
13 | #end
14 |
15 |
16 | class OpenFileDialogImpl extends OpenFileDialogBase {
17 | #if js
18 |
19 | private var _fileSelector:haxe.ui.util.html5.FileSelector = new haxe.ui.util.html5.FileSelector();
20 |
21 | public override function show() {
22 | var readMode = haxe.ui.util.html5.FileSelector.ReadMode.None;
23 | if (options.readContents == true) {
24 | if (options.readAsBinary == false) {
25 | readMode = haxe.ui.util.html5.FileSelector.ReadMode.Text;
26 | } else {
27 | readMode = haxe.ui.util.html5.FileSelector.ReadMode.Binary;
28 | }
29 | }
30 | _fileSelector.selectFile(onFileSelected, readMode, options.multiple, options.extensions);
31 | }
32 |
33 | private function onFileSelected(cancelled:Bool, files:Array) {
34 | if (cancelled == false) {
35 | dialogConfirmed(files);
36 | } else {
37 | dialogCancelled();
38 | }
39 | }
40 |
41 | #else
42 |
43 | private var _fr:FileReferenceList = null;
44 | private var _refToInfo:Map;
45 | private var _infos:Array;
46 |
47 | public override function show() {
48 | _refToInfo = new Map();
49 | _infos = [];
50 | _fr = new FileReferenceList();
51 | _fr.addEventListener(Event.SELECT, onSelect, false, 0, true);
52 | _fr.addEventListener(Event.CANCEL, onCancel, false, 0, true);
53 | _fr.browse(buildFileFilters());
54 | }
55 |
56 | private function buildFileFilters():Array {
57 | var f = null;
58 |
59 | if (options.extensions != null && options.extensions.length > 0) {
60 | f = [];
61 | for (e in options.extensions) {
62 | var ext = e.extension;
63 | ext = ext.trim();
64 | if (ext.length == 0) {
65 | continue;
66 | }
67 | var parts = ext.split(",");
68 | var finalParts = [];
69 | for (p in parts) {
70 | p = p.trim();
71 | if (p.length == 0) {
72 | continue;
73 | }
74 | finalParts.push("*." + p);
75 | }
76 |
77 | f.push(new FileFilter(e.label, finalParts.join(";")));
78 | }
79 | }
80 |
81 | return f;
82 | }
83 |
84 | private function onSelect(e:Event) {
85 | var fileList:Array = _fr.fileList;
86 | destroyFileRef();
87 | var infos:Array = [];
88 | for (fileRef in fileList) {
89 |
90 | var fullPath:String = null;
91 | #if sys
92 | fullPath = @:privateAccess fileRef.__path;
93 | #end
94 |
95 | var info:SelectedFileInfo = {
96 | isBinary: false,
97 | name: fileRef.name,
98 | fullPath: fullPath
99 | }
100 | if (options.readContents == true) {
101 | _refToInfo.set(fileRef, info);
102 | }
103 | infos.push(info);
104 | }
105 |
106 | if (options.readContents == false) {
107 | dialogConfirmed(infos);
108 | } else {
109 | for (fileRef in _refToInfo.keys()) {
110 | fileRef.addEventListener(Event.COMPLETE, onFileComplete, false, 0, true);
111 | fileRef.load();
112 | }
113 | }
114 |
115 | }
116 |
117 | private function onFileComplete(e:Event) {
118 | var fileRef = cast(e.target, FileReference);
119 | fileRef.removeEventListener(Event.COMPLETE, onFileComplete);
120 | var info = _refToInfo.get(fileRef);
121 | if (options.readAsBinary == true) {
122 | info.isBinary = true;
123 | info.bytes = Bytes.ofData(fileRef.data);
124 | } else {
125 | info.isBinary = false;
126 | info.text = fileRef.data.toString();
127 | }
128 |
129 | _infos.push(info);
130 | _refToInfo.remove(fileRef);
131 | if (isMapEmpty()) {
132 | var copy = _infos.copy();
133 | _infos = null;
134 | _refToInfo = null;
135 | dialogConfirmed(copy);
136 | }
137 | }
138 |
139 | private function isMapEmpty() {
140 | if (_refToInfo == null) {
141 | return true;
142 | }
143 |
144 | var n = 0;
145 | for (_ in _refToInfo.keys()) {
146 | n++;
147 | }
148 |
149 | return (n == 0);
150 | }
151 |
152 | private function onCancel(e:Event) {
153 | destroyFileRef();
154 | dialogCancelled();
155 | }
156 |
157 | private function destroyFileRef() {
158 | if (_fr == null) {
159 | return;
160 | }
161 |
162 | _fr.removeEventListener(Event.SELECT, onSelect);
163 | _fr.removeEventListener(Event.CANCEL, onCancel);
164 | _fr = null;
165 | }
166 |
167 | #end
168 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/PlatformImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import openfl.system.Capabilities;
4 |
5 | class PlatformImpl extends PlatformBase {
6 | public override function getSystemLocale():String {
7 | return Capabilities.language;
8 | }
9 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/SaveFileDialogImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | #if !js
4 | import openfl.events.Event;
5 | import openfl.net.FileFilter;
6 | import openfl.net.FileReference;
7 | import openfl.utils.ByteArray;
8 | #end
9 |
10 | class SaveFileDialogImpl extends SaveFileDialogBase {
11 | #if js
12 |
13 | private var _fileSaver:haxe.ui.util.html5.FileSaver = new haxe.ui.util.html5.FileSaver();
14 |
15 | public override function show() {
16 | if (fileInfo == null || (fileInfo.text == null && fileInfo.bytes == null)) {
17 | throw "Nothing to write";
18 | }
19 |
20 | if (fileInfo.text != null) {
21 | _fileSaver.saveText(fileInfo.name, fileInfo.text, onSaveResult);
22 | } else if (fileInfo.bytes != null) {
23 | _fileSaver.saveBinary(fileInfo.name, fileInfo.bytes, onSaveResult);
24 | }
25 | }
26 |
27 | private function onSaveResult(r:Bool) {
28 | if (r == true) {
29 | dialogConfirmed();
30 | } else {
31 | dialogCancelled();
32 | }
33 | }
34 |
35 | #else
36 |
37 | private var _fr:FileReference = null;
38 |
39 | public override function show() {
40 | if (fileInfo == null || (fileInfo.text == null && fileInfo.bytes == null)) {
41 | throw "Nothing to write";
42 | }
43 |
44 | var data:Dynamic = fileInfo.text;
45 | if (data == null) {
46 | data = ByteArray.fromBytes(fileInfo.bytes);
47 | }
48 | _fr = new FileReference();
49 | _fr.addEventListener(Event.SELECT, onSelect, false, 0, true);
50 | _fr.addEventListener(Event.CANCEL, onCancel, false, 0, true);
51 | _fr.save(data, fileInfo.name);
52 | }
53 |
54 | private function onSelect(e:Event) {
55 | destroyFileRef();
56 | dialogConfirmed();
57 | }
58 |
59 | private function onCancel(e:Event) {
60 | destroyFileRef();
61 | dialogCancelled();
62 | }
63 |
64 | private function destroyFileRef() {
65 | if (_fr == null) {
66 | return;
67 | }
68 |
69 | _fr.removeEventListener(Event.SELECT, onSelect);
70 | _fr.removeEventListener(Event.CANCEL, onCancel);
71 | _fr = null;
72 | }
73 |
74 | #end
75 | }
76 |
--------------------------------------------------------------------------------
/haxe/ui/backend/ScreenImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import flixel.FlxBasic;
4 | import flixel.FlxG;
5 | import flixel.FlxSprite;
6 | import flixel.group.FlxGroup;
7 | import flixel.group.FlxSpriteGroup;
8 | import haxe.ui.Toolkit;
9 | import haxe.ui.backend.flixel.CursorHelper;
10 | import haxe.ui.backend.flixel.KeyboardHelper;
11 | import haxe.ui.backend.flixel.MouseHelper;
12 | import haxe.ui.backend.flixel.StateHelper;
13 | import haxe.ui.core.Component;
14 | import haxe.ui.core.Screen;
15 | import haxe.ui.events.KeyboardEvent;
16 | import haxe.ui.events.MouseEvent;
17 | import haxe.ui.events.UIEvent;
18 | import haxe.ui.tooltips.ToolTipManager;
19 | import lime.system.System;
20 | import openfl.Lib;
21 |
22 | @:access(haxe.ui.backend.ComponentImpl)
23 | class ScreenImpl extends ScreenBase {
24 | private var _mapping:MapVoid>;
25 |
26 | public function new() {
27 | _mapping = new MapVoid>();
28 |
29 | MouseHelper.init();
30 | KeyboardHelper.init();
31 |
32 | FlxG.signals.postGameStart.add(onPostGameStart);
33 | FlxG.signals.preStateSwitch.add(onPreStateSwitch);
34 | FlxG.signals.postStateSwitch.add(onPostStateSwitch);
35 | FlxG.signals.preStateCreate.add(onPreStateCreate);
36 | onPostStateSwitch();
37 |
38 | addResizeHandler();
39 | }
40 |
41 | private function onPreStateCreate(state:flixel.FlxState) {
42 | if (!state.memberAdded.has(onMemberAdded)) {
43 | state.memberAdded.add(onMemberAdded);
44 | }
45 | checkMembers(state);
46 | if (!state.memberRemoved.has(onMemberRemoved)) {
47 | state.memberRemoved.add(onMemberRemoved);
48 | }
49 | }
50 |
51 | private function onPostGameStart() {
52 | onPostStateSwitch();
53 | }
54 |
55 | private function onPreStateSwitch() {
56 | if (FlxG.game == null) {
57 | return;
58 | }
59 | ToolTipManager.instance.reset();
60 | if (rootComponents != null) {
61 | while (rootComponents.length > 0) {
62 | var root = rootComponents[rootComponents.length - 1];
63 | removeComponent(root);
64 | }
65 | }
66 | rootComponents = [];
67 | }
68 |
69 | private function onPostStateSwitch() {
70 | if (FlxG.game == null) {
71 | return;
72 | }
73 |
74 | if (!FlxG.state.subStateOpened.has(onMemberAdded)) {
75 | FlxG.state.subStateOpened.add(onMemberAdded);
76 | }
77 | if (!FlxG.state.memberAdded.has(onMemberAdded)) {
78 | FlxG.state.memberAdded.add(onMemberAdded);
79 | }
80 | checkMembers(FlxG.state);
81 |
82 | if (!FlxG.state.memberRemoved.has(onMemberRemoved)) {
83 | FlxG.state.memberRemoved.add(onMemberRemoved);
84 | }
85 | if (!FlxG.state.subStateClosed.has(onMemberRemoved)) {
86 | FlxG.state.subStateClosed.add(onMemberRemoved);
87 | }
88 | }
89 |
90 | private function onMemberAdded(m:FlxBasic) {
91 | if ((m is Component)) {
92 | var c = cast(m, Component);
93 | if (rootComponents.indexOf(c) == -1) {
94 | addComponent(c);
95 | }
96 | } else if ((m is FlxTypedGroup)) {
97 | var group:FlxTypedGroup = cast m;
98 | checkMembers(group);
99 | }
100 | }
101 |
102 | private function onMemberRemoved(m:FlxBasic) {
103 | if ((m is Component) && rootComponents.indexOf(cast(m, Component)) != -1) {
104 | @:privateAccess var isDisposed = cast(m, Component)._isDisposed;
105 | removeComponent(cast m, isDisposed);
106 | }
107 | }
108 |
109 | private function checkMembers(state:FlxTypedGroup) {
110 | if (state == null || !state.exists) {
111 | return false;
112 | }
113 |
114 | var found = false; // we only want top level components
115 | for (m in state.members) {
116 | if ((m is Component)) {
117 | var c = cast(m, Component);
118 | if (rootComponents.indexOf(c) == -1) {
119 | addComponent(c);
120 | found = true;
121 | }
122 | } else if ((m is FlxTypedGroup)) {
123 | var group:FlxTypedGroup = cast m;
124 | group.memberAdded.addOnce(onMemberAdded);
125 | if (checkMembers(group) == true) {
126 | found = true;
127 | break;
128 | }
129 | } else if ((m is FlxTypedSpriteGroup)) {
130 | var spriteGroup:FlxTypedSpriteGroup = cast m;
131 | spriteGroup.group.memberAdded.addOnce(onMemberAdded);
132 | if (checkMembers(cast spriteGroup.group) == true) {
133 | found = true;
134 | break;
135 | }
136 | }
137 | }
138 | return found;
139 | }
140 |
141 | private override function get_width():Float {
142 | return FlxG.width / Toolkit.scaleX;
143 | }
144 |
145 | private override function get_height() {
146 | return FlxG.height / Toolkit.scaleY;
147 | }
148 |
149 | private override function get_actualWidth():Float {
150 | return FlxG.width;
151 | }
152 |
153 | private override function get_actualHeight():Float {
154 | return FlxG.height;
155 | }
156 |
157 | private override function get_dpi():Float {
158 | return System.getDisplay(0).dpi;
159 | }
160 |
161 | private override function get_title():String {
162 | return Lib.current.stage.window.title;
163 | }
164 |
165 | private override function set_title(s:String):String {
166 | Lib.current.stage.window.title = s;
167 | return s;
168 | }
169 |
170 | private var _cursor:String = null;
171 | public function setCursor(cursor:String, offsetX:Null = null, offsetY:Null = null) {
172 | #if (haxeui_flixel_no_custom_cursors || FLX_NO_MOUSE)
173 | return;
174 | #else
175 |
176 | if (!CursorHelper.useCustomCursors) {
177 | return;
178 | }
179 |
180 | if (_cursor == cursor) {
181 | return;
182 | }
183 |
184 | _cursor = cursor;
185 | if (CursorHelper.hasCursor(_cursor)) {
186 | var cursorInfo = CursorHelper.registeredCursors.get(_cursor);
187 | FlxG.mouse.load(CursorHelper.mouseLoadFunction(cursorInfo.graphic), cursorInfo.scale, cursorInfo.offsetX, cursorInfo.offsetY);
188 | } else if (openfl.Assets.exists(_cursor)) {
189 | FlxG.mouse.load(CursorHelper.mouseLoadFunction(_cursor), 1, offsetX, offsetY);
190 | } else {
191 | FlxG.mouse.load(null);
192 | }
193 | #end
194 | }
195 |
196 | public override function addComponent(component:Component):Component {
197 | if (rootComponents.contains(component) && StateHelper.currentState.members.contains(component)) {
198 | return component;
199 | }
200 |
201 | if (rootComponents.length > 0) {
202 | var cameras = StateHelper.findCameras(rootComponents[0]);
203 | if (cameras != null) {
204 | component.cameras = cameras;
205 | }
206 | }
207 |
208 | if (StateHelper.currentState.exists == true) {
209 | if (rootComponents.indexOf(component) == -1) {
210 | rootComponents.push(component);
211 | }
212 | StateHelper.currentState.add(component);
213 | component.state = StateHelper.currentState;
214 | onContainerResize();
215 | component.recursiveReady();
216 | component.syncComponentValidation();
217 | component.applyAddInternal();
218 | checkResetCursor();
219 | }
220 | return component;
221 | }
222 |
223 | public override function removeComponent(component:Component, dispose:Bool = true, invalidate:Bool = true):Component {
224 | if (@:privateAccess !component._allowDispose) {
225 | dispose = false;
226 | }
227 | if (rootComponents.indexOf(component) == -1) {
228 | if (dispose) {
229 | component.disposeComponent();
230 | }
231 | return component;
232 | }
233 | rootComponents.remove(component);
234 | if (rootComponents.indexOf(component) != -1) {
235 | throw "component wasnt actually removed from array, or there is a duplicate in the array";
236 | }
237 | if (StateHelper.currentState.exists == true) {
238 | StateHelper.currentState.remove(component, true);
239 | }
240 | // Destroying a sprite makes it get removed from its container (in this case, the state) without
241 | // being spliced, causing issues later with `addComponent()` not actually adding components on top
242 | // of everything else, so this has to go after the component has been properly removed.
243 | if (dispose) {
244 | component.disposeComponent();
245 | } else {
246 | component.applyRemoveInternal();
247 | }
248 | checkResetCursor();
249 | onContainerResize();
250 | return component;
251 | }
252 |
253 | private var _resizeHandlerAdded:Bool = false;
254 | private function addResizeHandler() {
255 | if (_resizeHandlerAdded == true) {
256 | return;
257 | }
258 | _resizeHandlerAdded = true;
259 | FlxG.signals.gameResized.add(onGameResized);
260 | }
261 |
262 | private function onGameResized(width:Int, height:Int) {
263 | onContainerResize();
264 | }
265 |
266 | private function onContainerResize() {
267 | for (c in rootComponents) {
268 | if (c.percentWidth > 0) {
269 | c.width = (this.width * c.percentWidth) / 100;
270 | }
271 | if (c.percentHeight > 0) {
272 | c.height = (this.height * c.percentHeight) / 100;
273 | }
274 | }
275 | }
276 |
277 | private override function handleSetComponentIndex(child:Component, index:Int) {
278 | var offset = 0;
279 | StateHelper.currentState.forEach((item) -> {
280 | offset++;
281 | });
282 | /* see: https://github.com/haxeui/haxeui-flixel/issues/61
283 | for (i in 0...StateHelper.currentState.length) {
284 | if ((StateHelper.currentState.members[i] is Component)) {
285 | offset = i;
286 | break;
287 | }
288 | }
289 | */
290 |
291 | StateHelper.currentState.remove(child, true);
292 | StateHelper.currentState.insert(index + offset, child);
293 | }
294 |
295 | private override function supportsEvent(type:String):Bool {
296 | if (type == MouseEvent.MOUSE_MOVE
297 | || type == MouseEvent.MOUSE_OVER
298 | || type == MouseEvent.MOUSE_OUT
299 | || type == MouseEvent.MOUSE_DOWN
300 | || type == MouseEvent.MOUSE_UP
301 | || type == MouseEvent.MOUSE_WHEEL
302 | || type == MouseEvent.CLICK
303 | || type == MouseEvent.DBL_CLICK
304 | || type == MouseEvent.RIGHT_CLICK
305 | || type == MouseEvent.RIGHT_MOUSE_DOWN
306 | || type == MouseEvent.RIGHT_MOUSE_UP
307 | || type == MouseEvent.MIDDLE_CLICK
308 | || type == MouseEvent.MIDDLE_MOUSE_DOWN
309 | || type == MouseEvent.MIDDLE_MOUSE_UP
310 | || type == UIEvent.RESIZE
311 | || type == KeyboardEvent.KEY_DOWN
312 | || type == KeyboardEvent.KEY_UP) {
313 | return true;
314 | }
315 | return false;
316 | }
317 |
318 | private override function mapEvent(type:String, listener:UIEvent->Void) {
319 | switch (type) {
320 | case MouseEvent.MOUSE_MOVE | MouseEvent.MOUSE_OVER | MouseEvent.MOUSE_OUT | MouseEvent.MOUSE_DOWN | MouseEvent.MOUSE_UP | MouseEvent.MOUSE_WHEEL | MouseEvent.CLICK | MouseEvent.DBL_CLICK | MouseEvent.RIGHT_CLICK | MouseEvent.RIGHT_MOUSE_DOWN | MouseEvent.RIGHT_MOUSE_UP | MouseEvent.MIDDLE_CLICK | MouseEvent.MIDDLE_MOUSE_DOWN | MouseEvent.MIDDLE_MOUSE_UP:
321 | if (_mapping.exists(type) == false) {
322 | _mapping.set(type, listener);
323 | MouseHelper.notify(type, __onMouseEvent, 10);
324 | }
325 |
326 | case KeyboardEvent.KEY_DOWN | KeyboardEvent.KEY_UP:
327 | if (_mapping.exists(type) == false) {
328 | _mapping.set(type, listener);
329 | KeyboardHelper.notify(type, __onKeyEvent, 10);
330 | }
331 | }
332 | }
333 |
334 | private function __onMouseEvent(event:MouseEvent) {
335 | var fn = _mapping.get(event.type);
336 | if (fn != null) {
337 | fn(event);
338 | }
339 | }
340 |
341 | private function __onKeyEvent(event:KeyboardEvent) {
342 | var fn = _mapping.get(event.type);
343 | if (fn != null) {
344 | fn(event);
345 | }
346 | }
347 |
348 | private function containsUnsolicitedMemberAt(x:Float, y:Float, state:FlxTypedGroup):Bool {
349 | if (state == null || !state.exists) {
350 | return false;
351 | }
352 |
353 | for (m in state.members) {
354 | if ((m is Component)) {
355 | var c:Component = cast m;
356 | if (c.hidden) {
357 | continue;
358 | }
359 | if (!c.hitTest(x, y, true)) {
360 | continue;
361 | }
362 | if (c._unsolicitedMembers != null && c._unsolicitedMembers.length > 0) {
363 | for (um in c._unsolicitedMembers) {
364 | var umx = um.sprite.x;
365 | var umy = um.sprite.y;
366 | var umw = um.sprite.width;
367 | var umh = um.sprite.height;
368 | if (x >= umx && y >= umy && x <= umx + umw && y <= umy + umh) {
369 | return true;
370 | }
371 | }
372 | }
373 | var spriteGroup:FlxTypedSpriteGroup = cast m;
374 | if (containsUnsolicitedMemberAt(x, y, cast spriteGroup.group) == true) {
375 | return true;
376 | }
377 | } else if ((m is FlxTypedSpriteGroup)) {
378 | var spriteGroup:FlxTypedSpriteGroup = cast m;
379 | if (!spriteGroup.visible) {
380 | continue;
381 | }
382 | if (containsUnsolicitedMemberAt(x, y, cast spriteGroup.group) == true) {
383 | return true;
384 | }
385 | } else if ((m is FlxSprite)) {
386 | var sprite:FlxSprite = cast m;
387 | var umx = sprite.x;
388 | var umy = sprite.y;
389 | var umw = sprite.width;
390 | var umh = sprite.height;
391 | if (x >= umx && y >= umy && x <= umx + umw && y <= umy + umh) {
392 | return true;
393 | }
394 | }
395 | }
396 |
397 | return false;
398 | }
399 |
400 | @:allow(haxe.ui.backend.flixel.MouseHelper)
401 | private function checkResetCursor(x:Null = null, y:Null = null) {
402 | if (x == null) {
403 | x = MouseHelper.currentWorldX;
404 | }
405 | if (y == null) {
406 | y = MouseHelper.currentWorldY;
407 | }
408 | var components = Screen.instance.findComponentsUnderPoint(x, y);
409 | components.reverse();
410 | var desiredCursor = "default";
411 | var desiredCursorOffsetX:Null = null;
412 | var desiredCursorOffsetY:Null = null;
413 | for (c in components) {
414 | if (c.style == null) {
415 | c.validateNow();
416 | }
417 | if (c.style.cursor != null) {
418 | desiredCursor = c.style.cursor;
419 | desiredCursorOffsetX = c.style.cursorOffsetX;
420 | desiredCursorOffsetX = c.style.cursorOffsetY;
421 | break;
422 | }
423 | }
424 |
425 | setCursor(desiredCursor, desiredCursorOffsetX, desiredCursorOffsetY);
426 | }
427 | }
428 |
--------------------------------------------------------------------------------
/haxe/ui/backend/TextDisplayImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import flixel.text.FlxText;
4 | import haxe.ui.Toolkit;
5 | import haxe.ui.util.Color;
6 |
7 | class TextDisplayImpl extends TextBase {
8 | private static inline var PADDING_X:Int = 2;
9 |
10 | public var tf:FlxText;
11 |
12 | public function new() {
13 | super();
14 | tf = new FlxText();
15 | tf.pixelPerfectRender = true;
16 | tf.autoSize = true;
17 | tf.active = false;
18 | }
19 |
20 | private override function validateData() {
21 | if (_text != null) {
22 | if (_dataSource == null) {
23 | tf.text = normalizeText(_text);
24 | }
25 | } else if (_htmlText != null) {
26 | var rules = [];
27 | var outText = processTags(_htmlText, rules);
28 | if (rules.length > 0) {
29 | tf.applyMarkup(outText, rules);
30 | } else {
31 | tf.text = normalizeText(_htmlText);
32 | }
33 | }
34 | }
35 |
36 | private override function validateStyle():Bool {
37 | var measureTextRequired:Bool = false;
38 | if (_textStyle != null) {
39 | if (_textStyle.textAlign != null) {
40 | //tf.autoSize = false;
41 | tf.alignment = _textStyle.textAlign;
42 | measureTextRequired = true;
43 | }
44 |
45 | if (_textStyle.fontSize != null) {
46 | tf.size = Std.int(_textStyle.fontSize * Toolkit.scale);
47 | measureTextRequired = true;
48 | }
49 |
50 | if (_fontInfo != null) {
51 | tf.font = _fontInfo.data;
52 | measureTextRequired = true;
53 | }
54 |
55 | if (_textStyle.fontBold != null) {
56 | tf.bold = _textStyle.fontBold;
57 | measureTextRequired = true;
58 | }
59 | if (_textStyle.fontItalic != null) {
60 | tf.italic = _textStyle.fontItalic;
61 | measureTextRequired = true;
62 | }
63 |
64 | if (_textStyle.color != null) {
65 | tf.color = _textStyle.color;
66 | }
67 |
68 | if (tf.wordWrap != _displayData.wordWrap) {
69 | tf.wordWrap = _displayData.wordWrap;
70 | //tf.autoSize = !_displayData.wordWrap;
71 | measureTextRequired = true;
72 | }
73 |
74 | if (tf.textField.multiline != _displayData.multiline) {
75 | tf.textField.multiline = _displayData.multiline;
76 | //tf.autoSize = !_displayData.multiline;
77 | measureTextRequired = true;
78 | }
79 | }
80 |
81 | return measureTextRequired;
82 | }
83 |
84 | private override function validateDisplay() {
85 | if (tf.textField.width != _width) {
86 | var width = _width * Toolkit.scaleX;
87 | tf.textField.width = (width >= 1 ? width : 1);
88 | }
89 | if (tf.textField.height != _height) {
90 | var height = _height * Toolkit.scaleY;
91 | tf.textField.height = (height >= 1 ? height : 1);
92 | }
93 | }
94 |
95 | private override function measureText() {
96 | _textWidth = (Math.fround(tf.textField.textWidth) + (PADDING_X * Toolkit.scaleX)) / Toolkit.scaleX;
97 | _textHeight = Math.fround(tf.textField.textHeight) / Toolkit.scaleY;
98 | if (_textHeight == 0) {
99 | var tmpText:String = tf.text;
100 | tf.text = "|";
101 | _textHeight = tf.textField.textHeight;
102 | tf.text = tmpText;
103 | }
104 | }
105 |
106 | private function normalizeText(text:String):String {
107 | text = StringTools.replace(text, "\\n", "\n");
108 | return text;
109 | }
110 |
111 | private override function get_supportsHtml():Bool {
112 | return true;
113 | }
114 |
115 | private static function processTags(s:String, rules:Array) {
116 | var inTag:Bool = false;
117 | var endTag:Bool = false;
118 | var tagDetails = "";
119 | var out = "";
120 |
121 | var tagStack:Array = [];
122 | var colorMap:Map = new Map();
123 |
124 | for (i in 0...s.length) {
125 | var c = s.charAt(i);
126 | switch (c) {
127 | case "<":
128 | var temp = s.substring(i + 1, i + 6);
129 | if (temp == "font " || temp == "/font") { // bit hacky!
130 | inTag = true;
131 | endTag = false;
132 | tagDetails = "";
133 | }
134 | case "/":
135 | if (inTag == true) {
136 | endTag = true;
137 | }
138 | case ">":
139 | if (inTag == true) {
140 | if (endTag == false) {
141 | var n = tagDetails.indexOf("color=");
142 | if (n != -1) {
143 | var col = tagDetails.substring(n + "color=".length);
144 | col = StringTools.replace(col, "'", "");
145 | col = StringTools.replace(col, "\"", "");
146 | tagStack.push(col);
147 | out += "<" + col + ">";
148 | colorMap.set("<" + col + ">", Color.fromString(col));
149 | } else {
150 | tagStack.push(tagDetails);
151 | out += tagDetails;
152 | }
153 |
154 | } else {
155 | var startTagDetails = tagStack.pop();
156 | out += "<" + startTagDetails + ">";
157 | }
158 | inTag = false;
159 | }
160 | default:
161 | if (inTag == true) {
162 | tagDetails += c.toLowerCase();
163 | } else {
164 | out += c;
165 | }
166 | }
167 | }
168 |
169 | for (k in colorMap.keys()) {
170 | rules.push(new FlxTextFormatMarkerPair(new FlxTextFormat(colorMap.get(k)), k));
171 | }
172 |
173 | return out;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/haxe/ui/backend/TextInputImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | typedef TextInputEvent = {type:String, stageX:Float, stageY:Float};
4 |
5 | #if (flixel >= "5.9.0")
6 | typedef TextInputImpl = haxe.ui.backend.flixel.textinputs.FlxTextInput;
7 | #else
8 | typedef TextInputImpl = haxe.ui.backend.flixel.textinputs.OpenFLTextInput;
9 | #end
10 |
--------------------------------------------------------------------------------
/haxe/ui/backend/TimerImpl.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | import openfl.events.Event;
4 | import openfl.Lib;
5 | import haxe.Timer;
6 |
7 | class TimerImpl {
8 | static private var __timers:Array = [];
9 |
10 | static public function update(e:Event) {
11 | var currentTime:Float = Timer.stamp();
12 | var count:Int = __timers.length;
13 | for (i in 0...count) {
14 | var timer:TimerImpl = __timers[i];
15 | if (timer._start <= currentTime && !timer._stopped) {
16 | timer._start = currentTime + (timer._delay / 1000);
17 | timer._callback();
18 | }
19 | }
20 |
21 | while (--count >= 0) {
22 | var timer:TimerImpl = __timers[count];
23 | if (timer._stopped) {
24 | timer._callback = null;
25 | __timers.remove(timer);
26 | }
27 | }
28 |
29 | if (__timers.length == 0) {
30 | Lib.current.stage.removeEventListener(Event.ENTER_FRAME, update);
31 | }
32 | }
33 |
34 | private var _callback:Void->Void;
35 | private var _start:Float;
36 | private var _stopped:Bool;
37 | private var _delay:Int;
38 |
39 | public function new(delay:Int, callback:Void->Void) {
40 | this._callback = callback;
41 | _delay = delay;
42 | _start = Timer.stamp() + (delay / 1000);
43 | __timers.push(this);
44 | if (__timers.length == 1) {
45 | Lib.current.stage.addEventListener(Event.ENTER_FRAME, update);
46 | }
47 | }
48 |
49 | public function stop() {
50 | _stopped = true;
51 | }
52 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/ToolkitOptions.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend;
2 |
3 | typedef ToolkitOptions = {
4 | }
5 |
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/CursorHelper.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import openfl.display.BitmapData;
4 |
5 | class CursorHelper {
6 | public static var useCustomCursors:Bool = true;
7 | public static var registeredCursors:Map = new Map();
8 |
9 | public static var mouseLoadFunction(default, set):String->BitmapData = (bmd) -> openfl.Assets.getBitmapData(bmd);
10 | static function set_mouseLoadFunction(v:String->BitmapData):String->BitmapData {
11 | if (v == null)
12 | return mouseLoadFunction;
13 |
14 | return mouseLoadFunction = v;
15 | }
16 |
17 | public static function registerCursor(name:String, graphic:String, scale:Float = 1, offsetX:Int = 0, offsetY:Int = 0) {
18 | registeredCursors.set(name, {
19 | name: name,
20 | graphic: graphic,
21 | scale: scale,
22 | offsetX: offsetX,
23 | offsetY: offsetY,
24 | });
25 | }
26 |
27 | public static function hasCursor(name:String):Bool {
28 | #if haxeui_flixel_no_custom_cursors
29 | return false;
30 | #end
31 |
32 | if (!useCustomCursors) {
33 | return false;
34 | }
35 | preregisterCursors();
36 | return registeredCursors.exists(name);
37 | }
38 |
39 | private static var _cursorsPreregistered:Bool = false;
40 | private static function preregisterCursors() {
41 | if (_cursorsPreregistered) {
42 | return;
43 | }
44 |
45 | _cursorsPreregistered = true;
46 |
47 | if (!registeredCursors.exists("pointer")) {
48 | registerCursor("pointer", "haxeui-flixel/styles/default/cursors/pointer.png", 1, -10, -1);
49 | }
50 | }
51 | }
52 |
53 | private typedef CursorInfo = {
54 | var name:String;
55 | var graphic:String;
56 | var scale:Float;
57 | var offsetX:Int;
58 | var offsetY:Int;
59 | }
60 |
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/FlxHaxeUIAppState.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import flixel.FlxG;
4 | import flixel.FlxState;
5 |
6 | class FlxHaxeUIAppState extends FlxState {
7 | public override function create() {
8 | if (Toolkit.backendProperties.getProp("haxe.ui.flixel.scaleMode") == "stage") {
9 | FlxG.scaleMode = new flixel.system.scaleModes.StageSizeScaleMode();
10 | }
11 |
12 | if (Toolkit.backendProperties.exists("haxe.ui.flixel.mouse.useSystemCursor")) {
13 | #if !FLX_NO_MOUSE
14 | FlxG.mouse.useSystemCursor = Toolkit.backendProperties.getPropBool("haxe.ui.flixel.mouse.useSystemCursor");
15 | #end
16 | }
17 |
18 | super.create();
19 |
20 | if (Toolkit.backendProperties.exists("haxe.ui.flixel.background.color")) {
21 | this.bgColor = Toolkit.backendProperties.getPropCol("haxe.ui.flixel.background.color") | 0xFF000000;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/FlxStyleHelper.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import flixel.FlxSprite;
4 | import flixel.graphics.frames.FlxFrame.FlxFrameAngle;
5 | import flixel.util.FlxColor;
6 | import flixel.util.FlxSpriteUtil;
7 | import haxe.ui.Toolkit;
8 | import haxe.ui.assets.ImageInfo;
9 | import haxe.ui.geom.Slice9;
10 | import haxe.ui.styles.Style;
11 | import haxe.ui.util.ColorUtil;
12 | import openfl.display.BitmapData;
13 | import openfl.geom.Matrix;
14 | import openfl.geom.Point;
15 | import openfl.geom.Rectangle;
16 |
17 | class FlxStyleHelper {
18 | public static function applyStyle(sprite:FlxSprite, style:Style):Bool {
19 | if (sprite == null || sprite.pixels == null) {
20 | return false;
21 | }
22 |
23 | var pixels:BitmapData = sprite.pixels;
24 |
25 | var left:Float = 0;
26 | var top:Float = 0;
27 | var width:Float = sprite.frameWidth;
28 | var height:Float = sprite.frameHeight;
29 |
30 | if (width <= 0 || height <= 0) {
31 | return false;
32 | }
33 |
34 | var rc:Rectangle = new Rectangle(top, left, width, height);
35 | pixels.fillRect(rc, 0x0);
36 |
37 | #if !no_openfl_drawing
38 | var useOpenFLDrawing = false;
39 | if (width > 1 && height > 1 && style.backgroundImage == null) {
40 | if ((style.borderRadius != null && (style.borderRadius > 0))
41 | || (style.borderRadiusTopLeft != null && (style.borderRadiusTopLeft > 0))
42 | || (style.borderRadiusTopRight != null && (style.borderRadiusTopRight > 0))
43 | || (style.borderRadiusBottomLeft != null && (style.borderRadiusBottomLeft > 0))
44 | || (style.borderRadiusBottomRight != null && (style.borderRadiusBottomRight > 0))
45 | ) {
46 | useOpenFLDrawing = true;
47 | }
48 | }
49 |
50 | if (useOpenFLDrawing) {
51 | var g = FlxSpriteUtil.flashGfx;
52 | var painted = OpenFLStyleHelper.paintStyleSection(g, style, width, height, left, top);
53 | FlxSpriteUtil.updateSpriteGraphic(sprite);
54 | return painted;
55 | }
56 | #end
57 |
58 | var painted = false;
59 |
60 | if (style.borderLeftSize != null && style.borderLeftSize != 0
61 | && style.borderLeftSize == style.borderRightSize
62 | && style.borderLeftSize == style.borderBottomSize
63 | && style.borderLeftSize == style.borderTopSize
64 |
65 | && style.borderLeftColor != null
66 | && style.borderLeftColor == style.borderRightColor
67 | && style.borderLeftColor == style.borderBottomColor
68 | && style.borderLeftColor == style.borderTopColor) {
69 | var borderSize = style.borderLeftSize * Toolkit.scale;
70 | var opacity = style.borderOpacity == null ? 1 : style.borderOpacity;
71 | var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.borderLeftColor;
72 |
73 | pixels.fillRect(new Rectangle(rc.left, rc.top, rc.width, borderSize), color); // top
74 | pixels.fillRect(new Rectangle(rc.right - borderSize, rc.top + borderSize, borderSize, rc.height - (borderSize * 2)), color); // right
75 | pixels.fillRect(new Rectangle(rc.left, rc.height - borderSize, rc.width, borderSize), color); // bottom
76 | pixels.fillRect(new Rectangle(rc.left, rc.top + borderSize, borderSize, rc.height - (borderSize * 2)), color); // left
77 | rc.inflate(-borderSize, -borderSize);
78 |
79 | painted = true;
80 | } else { // compound border
81 | var org = rc.clone();
82 |
83 | var borderTopSize:Float = 0;
84 | if (style.borderTopSize != null && style.borderTopSize > 0) {
85 | borderTopSize = style.borderTopSize * Toolkit.scale;
86 | }
87 | var borderLeftSize:Float = 0;
88 | if (style.borderLeftSize != null && style.borderLeftSize > 0) {
89 | borderLeftSize = style.borderLeftSize * Toolkit.scale;
90 | }
91 | var borderRightSize:Float = 0;
92 | if (style.borderRightSize != null && style.borderRightSize > 0) {
93 | borderRightSize = style.borderRightSize * Toolkit.scale;
94 | }
95 |
96 | if (style.borderTopSize != null && style.borderTopSize > 0) {
97 | var borderSize = style.borderTopSize * Toolkit.scale;
98 | var opacity = style.borderOpacity == null ? 1 : style.borderOpacity;
99 | var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.borderTopColor;
100 | pixels.fillRect(new Rectangle(rc.left + borderLeftSize, rc.top, org.width - (borderLeftSize + borderRightSize), borderSize), color); // top
101 | rc.top += borderSize;
102 |
103 | if (opacity > 0) {
104 | painted = true;
105 | }
106 | }
107 |
108 | if (style.borderBottomSize != null && style.borderBottomSize > 0) {
109 | var borderSize = style.borderBottomSize * Toolkit.scale;
110 | var opacity = style.borderOpacity == null ? 1 : style.borderOpacity;
111 | var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.borderBottomColor;
112 | pixels.fillRect(new Rectangle(rc.left, org.height - borderSize, rc.width, borderSize), color); // bottom
113 | rc.bottom -= borderSize;
114 |
115 | if (opacity > 0) {
116 | painted = true;
117 | }
118 | }
119 |
120 | if (style.borderLeftSize != null && style.borderLeftSize > 0) {
121 | var borderSize = style.borderLeftSize * Toolkit.scale;
122 | var opacity = style.borderOpacity == null ? 1 : style.borderOpacity;
123 | var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.borderLeftColor;
124 | pixels.fillRect(new Rectangle(rc.left, rc.top - borderTopSize, borderSize, org.height - rc.top + borderTopSize), color); // left
125 | rc.left += borderSize;
126 |
127 | if (opacity > 0) {
128 | painted = true;
129 | }
130 | }
131 |
132 | if (style.borderRightSize != null && style.borderRightSize > 0) {
133 | var borderSize = style.borderRightSize * Toolkit.scale;
134 | var opacity = style.borderOpacity == null ? 1 : style.borderOpacity;
135 | var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.borderRightColor;
136 | pixels.fillRect(new Rectangle(org.width - borderSize, rc.top - borderTopSize, borderSize, org.height + borderTopSize), color); // right
137 | rc.right -= borderSize;
138 |
139 | if (opacity > 0) {
140 | painted = true;
141 | }
142 | }
143 | }
144 |
145 |
146 | if (style.backgroundColor != null) {
147 | var opacity = style.backgroundOpacity == null ? 1 : style.backgroundOpacity;
148 | if (style.backgroundColorEnd != null && style.backgroundColor != style.backgroundColorEnd) {
149 | var gradientType:String = "vertical";
150 | if (style.backgroundGradientStyle != null) {
151 | gradientType = style.backgroundGradientStyle;
152 | }
153 |
154 | var arr:Array = null;
155 | var n:Int = 0;
156 | var rcLine:Rectangle = new Rectangle();
157 | if (gradientType == "vertical") {
158 | arr = ColorUtil.buildColorArray(style.backgroundColor, style.backgroundColorEnd, Std.int(rc.height));
159 | for (c in arr) {
160 | rcLine.setTo(rc.left, rc.top + n, rc.width, 1);
161 | pixels.fillRect(rcLine, Std.int(opacity * 0xFF) << 24 | c);
162 | n++;
163 | }
164 |
165 | if (opacity > 0 && n > 0) {
166 | painted = true;
167 | }
168 | } else if (gradientType == "horizontal") {
169 | arr = ColorUtil.buildColorArray(style.backgroundColor, style.backgroundColorEnd, Std.int(rc.width));
170 | for (c in arr) {
171 | rcLine.setTo(rc.left + n, rc.top, 1, rc.height);
172 | pixels.fillRect(rcLine, Std.int(opacity * 0xFF) << 24 | c);
173 | n++;
174 | }
175 |
176 | if (opacity > 0 && n > 0) {
177 | painted = true;
178 | }
179 | }
180 | } else {
181 | var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.backgroundColor;
182 | pixels.fillRect(rc, color);
183 |
184 | if (opacity > 0) {
185 | painted = true;
186 | }
187 | }
188 | }
189 |
190 | if (style.backgroundImage != null) {
191 | painted = true;
192 | Toolkit.assets.getImage(style.backgroundImage, function(info:ImageInfo) {
193 | if (info != null && info.data != null) {
194 | paintBackroundImage(sprite, info.data, style);
195 | }
196 | });
197 | }
198 |
199 | return painted;
200 | }
201 |
202 | private static function paintBackroundImage(sprite:FlxSprite, data:ImageData, style:Style) {
203 | var bmd = data.parent.bitmap;
204 | var rect = data.frame.copyToFlash();
205 |
206 | // if it's a spritesheet and the frame is rotated or flipped, paint the "original" sprite
207 | if (!bmd.rect.equals(rect) && (data.angle != FlxFrameAngle.ANGLE_0 || data.flipX || data.flipY)) {
208 | bmd = data.paintRotatedAndFlipped();
209 | rect.setTo(0, 0, data.sourceSize.x, data.sourceSize.y);
210 | }
211 |
212 | if (style.backgroundImageClipTop != null && style.backgroundImageClipBottom != null && style.backgroundImageClipLeft != null && style.backgroundImageClipRight != null) {
213 | rect.x += style.backgroundImageClipLeft;
214 | rect.y += style.backgroundImageClipTop;
215 | rect.width = Math.min(rect.width, style.backgroundImageClipRight - style.backgroundImageClipLeft);
216 | rect.height = Math.min(rect.height, style.backgroundImageClipBottom - style.backgroundImageClipTop);
217 | }
218 |
219 | var slice:haxe.ui.geom.Rectangle = null;
220 |
221 | if (style.backgroundImageSliceTop != null && style.backgroundImageSliceBottom != null && style.backgroundImageSliceLeft != null && style.backgroundImageSliceRight != null) {
222 | slice = new haxe.ui.geom.Rectangle(style.backgroundImageSliceLeft, style.backgroundImageSliceTop, style.backgroundImageSliceRight - style.backgroundImageSliceLeft, style.backgroundImageSliceBottom - style.backgroundImageSliceTop);
223 | }
224 |
225 | if (slice == null) {
226 |
227 | var matrix:Matrix = null;
228 |
229 | if (style.backgroundImageRepeat == "stretch") {
230 | matrix = new Matrix();
231 | matrix.translate( -rect.x, -rect.y);
232 | matrix.scale(sprite.frameWidth / rect.width, sprite.frameHeight / rect.height);
233 | }
234 |
235 | if (matrix == null) {
236 |
237 | var blitPt = new Point();
238 |
239 | if (style.backgroundImageRepeat == null) {
240 | sprite.pixels.copyPixels(bmd, rect, blitPt);
241 | }
242 |
243 | else if (style.backgroundImageRepeat == "repeat") {
244 |
245 | var repX = Math.ceil(sprite.frameWidth / rect.width);
246 | var repY = Math.ceil(sprite.frameHeight / rect.height);
247 |
248 | for (i in 0...repX) {
249 |
250 | blitPt.x = i * rect.width;
251 |
252 | for (j in 0...repY) {
253 |
254 | blitPt.y = j * rect.height;
255 |
256 | sprite.pixels.copyPixels(bmd, rect, blitPt);
257 | }
258 | }
259 | }
260 | }
261 |
262 | else {
263 | sprite.pixels.draw(bmd, matrix, null, null, null);
264 | }
265 |
266 | sprite.dirty = true;
267 | }
268 |
269 | else {
270 |
271 | var rects = Slice9.buildRects(sprite.frameWidth, sprite.frameHeight, rect.width, rect.height, slice);
272 |
273 | var src = null;
274 | for (i in 0...rects.src.length) {
275 |
276 | src = rects.src[i];
277 |
278 | src.left += rect.x;
279 | src.top += rect.y;
280 | }
281 |
282 | var srcOpenFL = new Rectangle();
283 | var dstOpenFL = new Rectangle();
284 | var dstPt = new Point();
285 | var mat = new Matrix();
286 |
287 | var pixels = sprite.pixels;
288 |
289 | // 9-slice credit @MSGhero and @IBwWG: https://gist.github.com/IBwWG/9ffe25a059983e7e4eeb7640d6645a37
290 |
291 | // copyPx() the corners
292 | pixels.copyPixels(bmd, setOpenFLRect(srcOpenFL, rects.src[0]), setOpenFLRect(dstOpenFL, rects.dst[0]).topLeft, null, null, true); // TL
293 | pixels.copyPixels(bmd, setOpenFLRect(srcOpenFL, rects.src[2]), setOpenFLRect(dstOpenFL, rects.dst[2]).topLeft, null, null, true); // TR
294 | pixels.copyPixels(bmd, setOpenFLRect(srcOpenFL, rects.src[6]), setOpenFLRect(dstOpenFL, rects.dst[6]).topLeft, null, null, true); // BL
295 | pixels.copyPixels(bmd, setOpenFLRect(srcOpenFL, rects.src[8]), setOpenFLRect(dstOpenFL, rects.dst[8]).topLeft, null, null, true); // BR
296 |
297 | // draw() the sides and center
298 | var tl = rects.src[0];
299 | var center = rects.src[4];
300 | var br = rects.src[8];
301 |
302 | var scaleH = rects.dst[4].width / center.width;
303 | var scaleV = rects.dst[4].height / center.height;
304 |
305 | // is it better to draw() from bmd (scaling a large bmd) or from a temp copyPx slice (creates a new bmd each time)?
306 |
307 | setOpenFLRect(dstOpenFL, rects.dst[1]);
308 | mat.translate(tl.width * (1 / scaleH - 1) - rect.x, -rect.y);
309 | mat.scale(scaleH, 1);
310 | pixels.draw(bmd, mat, null, null, dstOpenFL); // T
311 |
312 | mat.identity();
313 |
314 | setOpenFLRect(dstOpenFL, rects.dst[3]);
315 | mat.translate( -rect.x, tl.height * (1 / scaleV - 1) - rect.y);
316 | mat.scale(1, scaleV);
317 | pixels.draw(bmd, mat, null, null, dstOpenFL); // L
318 |
319 | mat.identity();
320 |
321 | setOpenFLRect(dstOpenFL, rects.dst[4]);
322 | mat.translate(tl.width * (1 / scaleH - 1) - rect.x, tl.height * (1 / scaleV - 1) - rect.y);
323 | mat.scale(scaleH, scaleV);
324 | pixels.draw(bmd, mat, null, null, dstOpenFL); // C
325 |
326 | mat.identity();
327 |
328 | setOpenFLRect(dstOpenFL, rects.dst[5]);
329 | mat.translate(sprite.frameWidth - rect.width - rect.x, tl.height * (1 / scaleV - 1) - rect.y);
330 | mat.scale(1, scaleV);
331 | pixels.draw(bmd, mat, null, null, dstOpenFL); // R
332 |
333 | mat.identity();
334 |
335 | setOpenFLRect(dstOpenFL, rects.dst[7]);
336 | mat.translate(tl.width * (1 / scaleH - 1) - rect.x, sprite.frameHeight - rect.height - rect.y);
337 | mat.scale(scaleH, 1);
338 | pixels.draw(bmd, mat, null, null, dstOpenFL); // B
339 | }
340 | }
341 |
342 | static function setOpenFLRect(oflRect:flash.geom.Rectangle, uiRect:haxe.ui.geom.Rectangle):flash.geom.Rectangle {
343 | oflRect.setTo(uiRect.left, uiRect.top, uiRect.width, uiRect.height);
344 | return oflRect;
345 | }
346 | }
347 |
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/InputManager.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 | import flixel.input.IFlxInputManager;
3 |
4 | class InputManager implements IFlxInputManager {
5 | public var onResetCb:Void->Void;
6 |
7 | public function new() {
8 | }
9 |
10 | public function reset():Void {
11 | if (onResetCb != null) {
12 | onResetCb();
13 | }
14 | }
15 | private function update():Void {
16 |
17 | }
18 | private function onFocus():Void {
19 |
20 | }
21 | private function onFocusLost():Void {
22 |
23 | }
24 | public function destroy():Void {
25 |
26 | }
27 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/KeyboardHelper.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import flixel.FlxG;
4 | import haxe.ui.core.Component;
5 | import haxe.ui.events.KeyboardEvent;
6 | import haxe.ui.focus.FocusManager;
7 |
8 | @:structInit
9 | class KeyboardCallback {
10 | public var fn:KeyboardEvent->Void;
11 | public var priority:Int;
12 | }
13 |
14 | class KeyboardHelper {
15 | private static var _initialized = false;
16 | private static var _callbacks:Map> = new Map>();
17 |
18 | public static function init() {
19 | if (_initialized == true) {
20 | return;
21 | }
22 |
23 | _initialized = true;
24 |
25 | FlxG.stage.addEventListener(openfl.events.KeyboardEvent.KEY_DOWN, onKeyDown);
26 | FlxG.stage.addEventListener(openfl.events.KeyboardEvent.KEY_UP, onKeyUp);
27 | }
28 |
29 | public static function notify(event:String, callback:KeyboardEvent->Void, priority:Int = 5) {
30 | var list = _callbacks.get(event);
31 | if (list == null) {
32 | list = new Array();
33 | _callbacks.set(event, list);
34 | }
35 |
36 | if (!hasCallback(list, callback)) {
37 | list.insert(0, {
38 | fn: callback,
39 | priority: priority
40 | });
41 |
42 | list.sort(function(a, b) {
43 | return a.priority - b.priority;
44 | });
45 | }
46 | }
47 |
48 | public static function remove(event:String, callback:KeyboardEvent->Void) {
49 | var list = _callbacks.get(event);
50 | if (list != null) {
51 | removeCallback(list, callback);
52 | if (list.length == 0) {
53 | _callbacks.remove(event);
54 | }
55 | }
56 | }
57 |
58 | private static function onKeyDown(e:openfl.events.KeyboardEvent) {
59 | dispatchEvent(KeyboardEvent.KEY_DOWN, e);
60 | }
61 |
62 | private static function onKeyUp(e:openfl.events.KeyboardEvent) {
63 | dispatchEvent(KeyboardEvent.KEY_UP, e);
64 | }
65 |
66 | private static function dispatchEvent(type:String, e:openfl.events.KeyboardEvent) {
67 | var event = new KeyboardEvent(type);
68 | event.keyCode = e.keyCode;
69 | event.altKey = e.altKey;
70 | event.ctrlKey = e.ctrlKey;
71 | event.shiftKey = e.shiftKey;
72 |
73 | var target = getTarget();
74 | // recreate a bubbling effect, so components will pass events onto their parents
75 | // can't use the `bubble` property as it causes a crash when `target` isn't the expected result, for example, on ListView.onRendererClick
76 | while (target != null) {
77 | if (target.hasEvent(event.type)) {
78 | target.dispatch(event);
79 | if (event.canceled == true) {
80 | return;
81 | }
82 | }
83 | target = target.parentComponent;
84 | }
85 |
86 | var list = _callbacks.get(type);
87 | if (list == null || list.length == 0) {
88 | return;
89 | }
90 |
91 | list = list.copy();
92 |
93 | for (l in list) {
94 | l.fn(event);
95 | if (event.canceled == true) {
96 | break;
97 | }
98 | }
99 | }
100 |
101 | private static function getTarget():Component {
102 | var target:Component = cast FocusManager.instance.focus;
103 | if (target != null && target.state == StateHelper.currentState) {
104 | return target;
105 | }
106 | return null;
107 | }
108 |
109 | private static function hasCallback(list:Array, fn:KeyboardEvent->Void):Bool {
110 | var has = false;
111 | for (item in list) {
112 | if (item.fn == fn) {
113 | has = true;
114 | break;
115 | }
116 | }
117 | return has;
118 | }
119 |
120 | private static function removeCallback(list:Array, fn:KeyboardEvent->Void) {
121 | var itemToRemove:KeyboardCallback = null;
122 | for (item in list) {
123 | if (item.fn == fn) {
124 | itemToRemove = item;
125 | break;
126 | }
127 | }
128 | if (itemToRemove != null) {
129 | list.remove(itemToRemove);
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/MouseHelper.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import flixel.FlxG;
4 | import haxe.ui.core.Component;
5 | import haxe.ui.core.Platform;
6 | import haxe.ui.core.Screen;
7 | import haxe.ui.events.MouseEvent;
8 |
9 | @:structInit
10 | class MouseCallback {
11 | public var fn:MouseEvent->Void;
12 | public var priority:Int;
13 | }
14 |
15 | class MouseHelper {
16 | public static var currentMouseX:Float = 0;
17 | public static var currentMouseY:Float = 0;
18 | public static var currentWorldX:Float = 0;
19 | public static var currentWorldY:Float = 0;
20 |
21 | private static var _initialized = false;
22 | private static var _callbacks:Map> = new Map>();
23 |
24 | private static var _mouseDownLeft:Dynamic;
25 | private static var _mouseDownMiddle:Dynamic;
26 | private static var _mouseDownRight:Dynamic;
27 | private static var _lastClickTarget:Dynamic;
28 | private static var _lastClickTime:Float;
29 | private static var _mouseOverTarget:Dynamic;
30 |
31 | public static function init() {
32 | if (_initialized == true) {
33 | return;
34 | }
35 |
36 | _initialized = true;
37 |
38 | FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_DOWN, onMouseDown);
39 | FlxG.stage.addEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_DOWN, onMouseDown);
40 | FlxG.stage.addEventListener(openfl.events.MouseEvent.MIDDLE_MOUSE_DOWN, onMouseDown);
41 |
42 | FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_UP, onMouseUp);
43 | FlxG.stage.addEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_UP, onMouseUp);
44 | FlxG.stage.addEventListener(openfl.events.MouseEvent.MIDDLE_MOUSE_UP, onMouseUp);
45 |
46 | FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_MOVE, onMouseMove);
47 |
48 | FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_WHEEL, onMouseWheel);
49 |
50 | FlxG.signals.preStateSwitch.add(onPreStateSwitched);
51 | }
52 |
53 | public static function notify(event:String, callback:MouseEvent->Void, priority:Int = 5) {
54 | var list = _callbacks.get(event);
55 | if (list == null) {
56 | list = new Array();
57 | _callbacks.set(event, list);
58 | }
59 |
60 | if (!hasCallback(list, callback)) {
61 | list.insert(0, {
62 | fn: callback,
63 | priority: priority
64 | });
65 |
66 | list.sort(function(a, b) {
67 | return a.priority - b.priority;
68 | });
69 | }
70 | }
71 |
72 | public static function remove(event:String, callback:MouseEvent->Void) {
73 | var list = _callbacks.get(event);
74 | if (list != null) {
75 | removeCallback(list, callback);
76 | if (list.length == 0) {
77 | _callbacks.remove(event);
78 | }
79 | }
80 | }
81 |
82 | private static function onPreStateSwitched() {
83 | // simulate mouse events when states switch to mop up any visual styles
84 | onMouse(MouseEvent.MOUSE_UP, currentMouseX, currentMouseY);
85 | onMouse(MouseEvent.MOUSE_MOVE, currentMouseX, currentMouseY);
86 | }
87 |
88 | private static function onMouseDown(e:openfl.events.MouseEvent) {
89 | var type = switch (e.type) {
90 | case openfl.events.MouseEvent.MIDDLE_MOUSE_DOWN: MouseEvent.MIDDLE_MOUSE_DOWN;
91 | case openfl.events.MouseEvent.RIGHT_MOUSE_DOWN: MouseEvent.RIGHT_MOUSE_DOWN;
92 | default: MouseEvent.MOUSE_DOWN;
93 | }
94 | onMouse(type, e.stageX, e.stageY, e.buttonDown, e.ctrlKey, e.shiftKey);
95 | }
96 |
97 | private static function onMouseUp(e:openfl.events.MouseEvent) {
98 | var type = switch (e.type) {
99 | case openfl.events.MouseEvent.MIDDLE_MOUSE_UP: MouseEvent.MIDDLE_MOUSE_UP;
100 | case openfl.events.MouseEvent.RIGHT_MOUSE_UP: MouseEvent.RIGHT_MOUSE_UP;
101 | default: MouseEvent.MOUSE_UP;
102 | }
103 | onMouse(type, e.stageX, e.stageY, e.buttonDown, e.ctrlKey, e.shiftKey);
104 | }
105 |
106 | private static function onMouseMove(e:openfl.events.MouseEvent) {
107 | onMouse(MouseEvent.MOUSE_MOVE, e.stageX, e.stageY, e.buttonDown, e.ctrlKey, e.shiftKey);
108 | }
109 |
110 | private static function onMouseWheel(e:openfl.events.MouseEvent) {
111 | var event = createEvent(MouseEvent.MOUSE_WHEEL, e.buttonDown, e.ctrlKey, e.shiftKey);
112 | event.delta = Math.max(-1, Math.min(1, e.delta));
113 |
114 | var target:Dynamic = getTarget(currentWorldX, currentWorldY);
115 |
116 | dispatchEvent(event, target);
117 | }
118 |
119 | private static function onMouse(type:String, x:Float, y:Float, buttonDown:Bool = false, ctrlKey:Bool = false, shiftKey:Bool = false) {
120 | if (currentMouseX != x) {
121 | currentMouseX = x;
122 | currentWorldX = ((currentMouseX - FlxG.scaleMode.offset.x) / (FlxG.scaleMode.scale.x * initialZoom())) / Toolkit.scaleX;
123 | }
124 | if (currentMouseY != y) {
125 | currentMouseY = y;
126 | currentWorldY = ((currentMouseY - FlxG.scaleMode.offset.y) / (FlxG.scaleMode.scale.y * initialZoom())) / Toolkit.scaleY;
127 | }
128 |
129 | var target:Dynamic = getTarget(currentWorldX, currentWorldY);
130 |
131 | if (target != _mouseOverTarget) {
132 | Screen.instance.checkResetCursor();
133 | if (_mouseOverTarget != null) {
134 | dispatchEventType(MouseEvent.MOUSE_OUT, buttonDown, ctrlKey, shiftKey, _mouseOverTarget);
135 | }
136 | dispatchEventType(MouseEvent.MOUSE_OVER, buttonDown, ctrlKey, shiftKey, target);
137 | _mouseOverTarget = target;
138 | }
139 |
140 | var clickType:String = null;
141 | switch (type) {
142 | case MouseEvent.MOUSE_DOWN:
143 | _mouseDownLeft = target;
144 |
145 | case MouseEvent.MIDDLE_MOUSE_DOWN:
146 | _mouseDownMiddle = target;
147 |
148 | case MouseEvent.RIGHT_MOUSE_DOWN:
149 | _mouseDownRight = target;
150 |
151 | case MouseEvent.MOUSE_UP:
152 | if (_mouseDownLeft == target) {
153 | clickType = MouseEvent.CLICK;
154 | }
155 | _mouseDownLeft = null;
156 |
157 | case MouseEvent.MIDDLE_MOUSE_UP:
158 | if (_mouseDownMiddle == target) {
159 | clickType = MouseEvent.MIDDLE_CLICK;
160 | }
161 | _mouseDownMiddle = null;
162 |
163 | case MouseEvent.RIGHT_MOUSE_UP:
164 | if (_mouseDownRight == target) {
165 | clickType = MouseEvent.RIGHT_CLICK;
166 | }
167 | _mouseDownRight = null;
168 | }
169 |
170 | dispatchEventType(type, buttonDown, ctrlKey, shiftKey, target);
171 |
172 | if (clickType != null) {
173 | dispatchEventType(clickType, buttonDown, ctrlKey, shiftKey, target);
174 |
175 | if (type == MouseEvent.MOUSE_UP) {
176 | var currentTime = Timer.stamp();
177 | if (currentTime - _lastClickTime < 0.5 && target == _lastClickTarget) {
178 | dispatchEventType(MouseEvent.DBL_CLICK, buttonDown, ctrlKey, shiftKey, target);
179 | _lastClickTime = 0;
180 | _lastClickTarget = null;
181 | } else {
182 | _lastClickTarget = target;
183 | _lastClickTime = currentTime;
184 | }
185 | }
186 | }
187 | }
188 |
189 | private static function dispatchEventType(type:String, buttonDown:Bool, ctrlKey:Bool, shiftKey:Bool, target:Dynamic) {
190 | var event = createEvent(type, buttonDown, ctrlKey, shiftKey);
191 | dispatchEvent(event, target);
192 | }
193 |
194 | private static function dispatchEvent(event:MouseEvent, target:Dynamic) {
195 | if ((target is Component)) {
196 | var c:Component = cast target;
197 | // recreate a bubbling effect, so components will pass events onto their parents
198 | // can't use the `bubble` property as it causes a crash when `target` isn't the expected result, for example, on ListView.onRendererClick
199 | while (c != null) {
200 | if (c.hasEvent(event.type)) {
201 | c.dispatch(event);
202 | if (event.canceled == true) {
203 | return;
204 | }
205 | }
206 | c = c.parentComponent;
207 | }
208 | }
209 |
210 | var list = _callbacks.get(event.type);
211 | if (list == null || list.length == 0) {
212 | return;
213 | }
214 |
215 | list = list.copy();
216 |
217 | for (l in list) {
218 | l.fn(event);
219 | if (event.canceled == true) {
220 | break;
221 | }
222 | }
223 | }
224 |
225 | private static function createEvent(type:String, buttonDown:Bool, ctrlKey:Bool, shiftKey:Bool):MouseEvent {
226 | var event = new MouseEvent(type);
227 | event.screenX = currentWorldX;
228 | event.screenY = currentWorldY;
229 | event.buttonDown = buttonDown;
230 | event.touchEvent = Platform.instance.isMobile;
231 | event.ctrlKey = ctrlKey;
232 | event.shiftKey = shiftKey;
233 | return event;
234 | }
235 |
236 | private static function getTarget(x:Float, y:Float):Dynamic {
237 | var components = Screen.instance.findComponentsUnderPoint(x, y);
238 | components.reverse();
239 | for (c in components) {
240 | if (c.state == StateHelper.currentState) {
241 | return c;
242 | }
243 | }
244 | return Screen.instance;
245 | }
246 |
247 | private static inline function initialZoom():Float {
248 | #if (flixel <= "4.11.0") // FlxG.initialZoom removed in later versions of flixel
249 |
250 | return FlxG.initialZoom;
251 |
252 | #else
253 |
254 | return 1;
255 |
256 | #end
257 | }
258 |
259 | private static function hasCallback(list:Array, fn:MouseEvent->Void):Bool {
260 | var has = false;
261 | for (item in list) {
262 | if (item.fn == fn) {
263 | has = true;
264 | break;
265 | }
266 | }
267 | return has;
268 | }
269 |
270 | private static function removeCallback(list:Array, fn:MouseEvent->Void) {
271 | var itemToRemove:MouseCallback = null;
272 | for (item in list) {
273 | if (item.fn == fn) {
274 | itemToRemove = item;
275 | break;
276 | }
277 | }
278 | if (itemToRemove != null) {
279 | list.remove(itemToRemove);
280 | }
281 | }
282 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/OpenFLStyleHelper.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import haxe.ui.styles.Style;
4 | import openfl.display.GradientType;
5 | import openfl.display.Graphics;
6 | import openfl.display.InterpolationMethod;
7 | import openfl.display.SpreadMethod;
8 | import openfl.geom.Matrix;
9 | import openfl.geom.Rectangle;
10 |
11 | class OpenFLStyleHelper {
12 | public function new() {
13 | }
14 |
15 | public static function paintStyleSection(graphics:Graphics, style:Style, width:Float, height:Float, left:Float = 0, top:Float = 0, clear:Bool = true):Bool {
16 | if (clear == true) {
17 | graphics.clear();
18 | }
19 |
20 | if (width <= 0 || height <= 0) {
21 | return false;
22 | }
23 |
24 | /*
25 | left = Math.fround(left);
26 | top = Math.fround(top);
27 | width = Math.fround(width);
28 | height = Math.fround(height);
29 | */
30 |
31 | left = Std.int(left);
32 | top = Std.int(top);
33 | width = Std.int(width);
34 | height = Std.int(height);
35 |
36 | var hasFullStyledBorder:Bool = false;
37 | var borderStyle = style.borderStyle;
38 | if (borderStyle == null) {
39 | borderStyle = "solid";
40 | }
41 |
42 | var rc:Rectangle = new Rectangle(top, left, width, height);
43 | var borderRadius:Float = 0;
44 | if (style.borderRadius != null) {
45 | borderRadius = style.borderRadius * Toolkit.scale;
46 | }
47 |
48 | var painted = false;
49 |
50 | if (style.borderLeftSize != null && style.borderLeftSize != 0
51 | && style.borderLeftSize == style.borderRightSize
52 | && style.borderLeftSize == style.borderBottomSize
53 | && style.borderLeftSize == style.borderTopSize
54 |
55 | && style.borderLeftColor != null
56 | && style.borderLeftColor == style.borderRightColor
57 | && style.borderLeftColor == style.borderBottomColor
58 | && style.borderLeftColor == style.borderTopColor) { // full border
59 |
60 | graphics.lineStyle(style.borderLeftSize * Toolkit.scale, style.borderLeftColor);
61 | rc.left += (style.borderLeftSize * Toolkit.scale) / 2;
62 | rc.top += (style.borderLeftSize * Toolkit.scale) / 2;
63 | rc.bottom -= (style.borderLeftSize * Toolkit.scale) / 2;
64 | rc.right -= (style.borderLeftSize * Toolkit.scale) / 2;
65 | //rc.inflate( -(style.borderLeftSize / 2), -(style.borderLeftSize / 2));
66 |
67 | painted = true;
68 | } else { // compound border
69 | if ((style.borderTopSize != null && style.borderTopSize > 0)
70 | || (style.borderBottomSize != null && style.borderBottomSize > 0)
71 | || (style.borderLeftSize != null && style.borderLeftSize > 0)
72 | || (style.borderRightSize != null && style.borderRightSize > 0)) {
73 |
74 | var org = rc.clone();
75 |
76 | if (style.borderTopSize != null && style.borderTopSize > 0) {
77 | graphics.beginFill(style.borderTopColor);
78 | graphics.drawRect(0, 0, org.width, (style.borderTopSize * Toolkit.scale));
79 | graphics.endFill();
80 |
81 | rc.top += (style.borderTopSize * Toolkit.scale);
82 | }
83 |
84 | if (style.borderBottomSize != null && style.borderBottomSize > 0) {
85 | graphics.beginFill(style.borderBottomColor);
86 | graphics.drawRect(0, org.height - (style.borderBottomSize * Toolkit.scale), org.width, (style.borderBottomSize * Toolkit.scale));
87 | graphics.endFill();
88 |
89 | rc.bottom -= (style.borderBottomSize * Toolkit.scale);
90 | }
91 |
92 | if (style.borderLeftSize != null && style.borderLeftSize > 0) {
93 | graphics.beginFill(style.borderLeftColor);
94 | graphics.drawRect(0, rc.top, (style.borderLeftSize * Toolkit.scale), org.height - rc.top);
95 | graphics.endFill();
96 |
97 | rc.left += (style.borderLeftSize * Toolkit.scale);
98 | }
99 |
100 | if (style.borderRightSize != null && style.borderRightSize > 0) {
101 | graphics.beginFill(style.borderRightColor);
102 | graphics.drawRect(org.width - (style.borderRightSize * Toolkit.scale), rc.top, (style.borderRightSize * Toolkit.scale), org.height - rc.top);
103 | graphics.endFill();
104 |
105 | rc.right -= (style.borderRightSize * Toolkit.scale);
106 | }
107 |
108 | painted = true;
109 | }
110 | }
111 |
112 | var backgroundColor:Null = style.backgroundColor;
113 | var backgroundColorEnd:Null = style.backgroundColorEnd;
114 | var backgroundOpacity:Null = style.backgroundOpacity;
115 | #if html5 // TODO: fix for html5 not working with non-gradient fills
116 | if (backgroundColor != null && backgroundColorEnd == null) {
117 | backgroundColorEnd = backgroundColor;
118 | }
119 | #end
120 |
121 | if(backgroundOpacity == null) {
122 | backgroundOpacity = 1;
123 | }
124 |
125 | if (backgroundColor != null) {
126 | if (backgroundColorEnd != null) {
127 | var w:Int = Std.int(rc.width);
128 | var h:Int = Std.int(rc.height);
129 | var colors:Array = [backgroundColor, backgroundColorEnd];
130 | var alphas:Array = [backgroundOpacity, backgroundOpacity];
131 | var ratios:Array = [0, 255];
132 | var matrix:Matrix = new Matrix();
133 |
134 | var gradientType:String = "vertical";
135 | if (style.backgroundGradientStyle != null) {
136 | gradientType = style.backgroundGradientStyle;
137 | }
138 |
139 | if (gradientType == "vertical") {
140 | matrix.createGradientBox(w - 2, h - 2, Math.PI / 2, 0, 0);
141 | } else if (gradientType == "horizontal") {
142 | matrix.createGradientBox(w - 2, h - 2, 0, 0, 0);
143 | }
144 |
145 | graphics.beginGradientFill(GradientType.LINEAR,
146 | colors,
147 | alphas,
148 | ratios,
149 | matrix,
150 | SpreadMethod.PAD,
151 | InterpolationMethod.LINEAR_RGB,
152 | 0);
153 | } else {
154 | graphics.beginFill(backgroundColor, backgroundOpacity);
155 | }
156 |
157 | if (backgroundOpacity > 0) {
158 | painted = true;
159 | }
160 | }
161 |
162 | if (borderRadius == 0) {
163 | if (style.borderRadiusTopLeft != null || style.borderRadiusTopRight != null || style.borderRadiusBottomLeft != null || style.borderRadiusBottomRight != null) {
164 | graphics.drawRoundRectComplex(rc.left, rc.top, rc.width, rc.height, style.borderRadiusTopLeft, style.borderRadiusTopRight, style.borderRadiusBottomLeft, style.borderRadiusBottomRight);
165 | } else if (hasFullStyledBorder) {
166 | graphics.drawRect(rc.left, rc.top, rc.width, rc.height);
167 | } else {
168 | graphics.drawRect(rc.left, rc.top, rc.width, rc.height);
169 | }
170 | } else {
171 | if (rc.width == rc.height && borderRadius >= rc.width / 2) {
172 | borderRadius = rc.width - 1;
173 | }
174 | graphics.drawRoundRect(rc.left, rc.top, rc.width, rc.height, borderRadius + 1, borderRadius + 1);
175 | }
176 |
177 | graphics.endFill();
178 |
179 | return painted;
180 | }
181 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/StateHelper.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import flixel.FlxBasic;
4 | import flixel.FlxCamera;
5 | import flixel.FlxG;
6 | import flixel.FlxState;
7 | import flixel.group.FlxGroup;
8 | import flixel.group.FlxSpriteGroup;
9 |
10 | class StateHelper {
11 | public static var currentState(get, null):FlxState;
12 | private static function get_currentState():FlxState {
13 | var s:FlxState = FlxG.state;
14 | if (s != null && s.subState != null) {
15 | var r = s.subState;
16 | while (r != null) {
17 | if (r.subState == null) {
18 | break;
19 | }
20 | r = r.subState;
21 | }
22 | s = r;
23 | }
24 | return s;
25 | }
26 |
27 | public static function hasMember(member:FlxBasic, group:FlxTypedGroup = null):Bool {
28 | // TODO: we drop frames because of this, need to revise / thing of a better way (or simply remove)
29 | return true;
30 |
31 | if (group == null) {
32 | group = currentState;
33 | }
34 |
35 | if (group == null || !group.exists) {
36 | return false;
37 | }
38 |
39 | if (member == group) {
40 | return true;
41 | }
42 |
43 | for (m in group.members) {
44 | if (m == member) {
45 | return true;
46 | }
47 |
48 | if ((m is FlxTypedGroup)) {
49 | if (hasMember(member, cast m) == true) {
50 | return true;
51 | }
52 | } else if ((m is FlxSpriteGroup)) {
53 | if (groupHasMember(member, cast m) == true) {
54 | return true;
55 | }
56 | }
57 | }
58 |
59 | return false;
60 | }
61 |
62 | private static function groupHasMember(member:FlxBasic, group:FlxSpriteGroup) {
63 | if (group == null || !group.exists) {
64 | return false;
65 | }
66 |
67 | if (member == group) {
68 | return true;
69 | }
70 |
71 | for (m in group.members) {
72 | if (m == member) {
73 | return true;
74 | }
75 |
76 | if ((m is FlxTypedGroup)) {
77 | if (hasMember(member, cast m) == true) {
78 | return true;
79 | }
80 | } else if ((m is FlxSpriteGroup)) {
81 | if (groupHasMember(member, cast m) == true) {
82 | return true;
83 | }
84 | }
85 | }
86 |
87 | return false;
88 | }
89 |
90 | public static function findCameras(member:FlxBasic, group:FlxTypedGroup = null):Array {
91 | if (group == null) {
92 | group = currentState;
93 | }
94 |
95 | if (group == null || !group.exists) {
96 | return null;
97 | }
98 |
99 | for (m in group.members) {
100 | if (m != null && m.cameras != null && m.cameras.length > 0) {
101 | if (m == member || ((m is FlxTypedGroup) && hasMember(member, cast m))) {
102 | return m.cameras;
103 | }
104 | }
105 | }
106 |
107 | return null;
108 | }
109 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UIFragment.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | class UIFragment extends UIFragmentBase {
4 |
5 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UIFragmentBase.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | typedef UIFragmentBase = flixel.group.FlxSpriteContainer;
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UIRTTITools.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import haxe.ui.RuntimeComponentBuilder;
4 | import haxe.ui.core.Component;
5 | import haxe.rtti.CType;
6 | import haxe.ui.core.ComponentClassMap;
7 |
8 | using StringTools;
9 |
10 | class UIRTTITools {
11 | public static function buildViaRTTI(rtti:Classdef):Component {
12 | var root:Component = null;
13 | var m = getMetaWithValueRTTI(rtti.meta, "build", "haxe.ui.RuntimeComponentBuilder.build");
14 | if (m != null) {
15 | var assetId = m.params[0].replace("haxe.ui.RuntimeComponentBuilder.build(", "").replace(")", "");
16 | assetId = assetId.replace("\"", "");
17 | assetId = assetId.replace("'", "");
18 | root = RuntimeComponentBuilder.fromAsset(assetId);
19 | if (root == null) {
20 | throw "could not loading runtime ui from asset (" + assetId + ")";
21 | }
22 | }
23 | m = getMetaRTTI(rtti.meta, "xml");
24 | if (m != null) { // comes back as an escaped CDATA section
25 | var xmlString = m.params[0].trim();
26 | if (xmlString.startsWith("")) {
30 | xmlString = xmlString.substring(0, xmlString.length - "]]>".length);
31 | }
32 | if (xmlString.startsWith("\"")) {
33 | xmlString = xmlString.substring(1);
34 | }
35 | if (xmlString.endsWith("\"")) {
36 | xmlString = xmlString.substring(0, xmlString.length - 1);
37 | }
38 | xmlString = xmlString.replace("\\r", "\r");
39 | xmlString = xmlString.replace("\\n", "\n");
40 | xmlString = xmlString.replace("\\\"", "\"");
41 | xmlString = xmlString.trim();
42 | try {
43 | root = RuntimeComponentBuilder.fromString(xmlString);
44 | } catch (e:Dynamic) {
45 | trace("ERROR", e);
46 | trace(haxe.CallStack.toString(haxe.CallStack.exceptionStack(true)));
47 | }
48 | }
49 | return root;
50 | }
51 |
52 | public static function linkViaRTTI(rtti:Classdef, target:Dynamic, root:Component, force:Bool = false) {
53 | if (root == null) {
54 | return;
55 | }
56 | for (f in rtti.fields) {
57 | switch (f.type) {
58 | case CClass(name, params):
59 | if (ComponentClassMap.instance.hasClassName(name)) {
60 | var candidate = root.findComponent(f.name);
61 | if (force) {
62 | Reflect.setField(target, f.name, null);
63 | }
64 | if (candidate != null && Reflect.field(target, f.name) == null) {
65 | var temp = Type.createEmptyInstance(Type.resolveClass(name));
66 | if ((temp is IComponentDelegate)) {
67 | var componentDelegate:IComponentDelegate = Type.createEmptyInstance(Type.resolveClass(name));
68 | componentDelegate.component = candidate;
69 | Reflect.setField(target, f.name, componentDelegate);
70 | } else {
71 | Reflect.setField(target, f.name, candidate);
72 | }
73 | }
74 | }
75 | case CFunction(args, ret):
76 | var m = getMetaRTTI(f.meta, "bind");
77 | if (m != null) {
78 | if (m.params[0] == "this") {
79 | if ((target is IComponentDelegate)) {
80 | var componentDelegate:IComponentDelegate = cast target;
81 | bindEvent(rtti, componentDelegate.component, f.name, target, m.params[1], true);
82 | } else {
83 | bindEvent(rtti, root, f.name, target, m.params[1]);
84 | }
85 | } else {
86 | var candidate:Component = root.findComponent(m.params[0]);
87 | if (candidate != null) {
88 | bindEvent(rtti, candidate, f.name, target, m.params[1]);
89 | } else {
90 | // another perfectly valid contruct, albeit less common (though still useful), is the ability to use
91 | // @:bind to bind to variables on static fields, eg:
92 | // @:bind(MyClass.instance, SomeEvent.EventType)
93 | // this code facilitates that by attempting to resolve the item and binding the event to it
94 | var parts = m.params[0].split(".");
95 | var className = parts.shift();
96 | var c = Type.resolveClass(className);
97 |
98 | if (c == null) {
99 | // this allows full qualified class names:
100 | // @:bind(some.pkg.MyClass.instance, SomeEvent.EventType)
101 | // by looping over each part looking for a valid class
102 | // its not fast (or pretty), but it will only happen once (per @:bind)
103 | var candidateClass = className;
104 | while (parts.length > 0) {
105 | var part = parts.shift();
106 | candidateClass += "." + part;
107 | var temp = Type.resolveClass(candidateClass);
108 | if (temp != null) {
109 | c = temp;
110 | break;
111 | }
112 | }
113 | }
114 |
115 | if (c != null) {
116 | var ref:Dynamic = c;
117 | var found = (parts.length > 0);
118 | for (part in parts) {
119 | if (!Reflect.hasField(ref, part)) {
120 | found = false;
121 | break;
122 | }
123 | ref = Reflect.field(ref, part);
124 | }
125 | if (found) {
126 | if (ref != null) {
127 | if ((ref is UIRuntimeState)) {
128 | var state:UIRuntimeState = cast ref;
129 | bindEvent(rtti, state.root, f.name, target, m.params[1]);
130 | } else if ((ref is UIRuntimeSubState)) {
131 | var subState:UIRuntimeSubState = cast ref;
132 | bindEvent(rtti, subState.root, f.name, target, m.params[1]);
133 | } else if ((ref is IComponentDelegate)) {
134 | var componentDelegate:IComponentDelegate = cast ref;
135 | bindEvent(rtti, componentDelegate.component, f.name, target, m.params[1]);
136 | } else if ((ref is Component)) {
137 | var component:Component = cast ref;
138 | bindEvent(rtti, component, f.name, target, m.params[1]);
139 | }
140 | } else {
141 | throw "bind param resolved, but was null '" + m.params[0] + "'";
142 | }
143 | } else {
144 | throw "could not resolve bind param '" + m.params[0] + "'";
145 | }
146 | } else {
147 | throw "could not resolve class '" + className + "'";
148 | }
149 | }
150 | }
151 | }
152 | case _:
153 | }
154 | }
155 | }
156 |
157 | private static function bindEvent(rtti:Classdef, candidate:Component, fieldName:String, target:Dynamic, eventClass:String, isComponentDelegate:Bool = false) {
158 | if (candidate == null) {
159 | return;
160 | }
161 | var parts = eventClass.split(".");
162 | var eventName = parts.pop();
163 | eventClass = parts.join(".");
164 | var c = resolveEventClass(rtti, eventClass);
165 | if (c != null) {
166 | var eventString = Reflect.field(c, eventName);
167 | var fn = Reflect.field(target, fieldName);
168 | // this may be ill-concieved, but if we are talking about a component delegate (ie, a fragment)
169 | // it means we are going to attach a component to an "empty" class which means this code has
170 | // already run once, meaning there are two event listeners, this way we remove them first
171 | // in practice its probably _exactly_ what we want, but this could also clear up binding
172 | // two functions to the same event (which isnt common at all)
173 | if (isComponentDelegate) {
174 | candidate.unregisterEvents(eventString);
175 | }
176 | candidate.registerEvent(eventString, fn);
177 | } else {
178 | throw "could not resolve event class '" + eventClass + "' (you may need to use fully qualified class names)";
179 | }
180 | }
181 |
182 | private static function resolveEventClass(rtti:Classdef, eventClass:String) {
183 | var candidateEvent = "haxe.ui.events." + eventClass;
184 | var event = Type.resolveClass(candidateEvent);
185 | if (event != null) {
186 | return event;
187 | }
188 |
189 | var event = Type.resolveClass(eventClass);
190 | if (event != null) {
191 | return event;
192 | }
193 |
194 | // this is pretty brute force method, were going to see if we can find any functions
195 | // with @:bind meta, these are presumably the event handlers, if we can find one
196 | // where the the last part of the arg type (which would be the event type) matches
197 | // the event we are looking for, we'll consider that match, and can use that as a
198 | // fully qualified event class
199 | for (f in rtti.fields) {
200 | switch (f.type) {
201 | case CFunction(args, ret):
202 | if (getMetaRTTI(f.meta, "bind") != null) {
203 | for (arg in args) {
204 | switch (arg.t) {
205 | case CClass(name, params):
206 | if (name.endsWith(eventClass)) {
207 | var event = Type.resolveClass(name);
208 | if (event != null) {
209 | return event;
210 | }
211 | }
212 | case _:
213 | }
214 | }
215 | }
216 | case _:
217 | }
218 | }
219 |
220 | return null;
221 | }
222 |
223 | private static function getMetaRTTI(metadata:MetaData, name:String):{name:String, params:Array} {
224 | for (m in metadata) {
225 | if (m.name == name || m.name == ":" + name) {
226 | return m;
227 | }
228 | }
229 | return null;
230 | }
231 |
232 | private static function getMetasRTTI(metadata:MetaData, name:String):Array<{name:String, params:Array}> {
233 | var metas = [];
234 | for (m in metadata) {
235 | if (m.name == name || m.name == ":" + name) {
236 | metas.push(m);
237 | }
238 | }
239 | return metas;
240 | }
241 |
242 | private static function getMetaWithValueRTTI(metadata:MetaData, name:String, value:String, paramIndex:Int = 0):{name:String, params:Array} {
243 | for (m in metadata) {
244 | if (m.name == name || m.name == ":" + name) {
245 | if (m.params[paramIndex].startsWith(value)) {
246 | return m;
247 | }
248 | }
249 | }
250 | return null;
251 | }
252 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UIRuntimeFragment.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import haxe.ui.RuntimeComponentBuilder;
4 | import haxe.ui.core.Component;
5 | import haxe.ui.events.UIEvent;
6 | import haxe.ui.events.EventType;
7 | import haxe.ui.backend.flixel.UIRTTITools.*;
8 |
9 | using StringTools;
10 |
11 | @:rtti
12 | class UIRuntimeFragment extends UIFragmentBase implements IComponentDelegate { // uses rtti to "build" a class with a similar experience to using macros
13 | public var root:Component;
14 |
15 | public function new() {
16 | super();
17 |
18 | scrollFactor.set(0, 0); // ui doesn't scroll by default
19 |
20 | var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this));
21 | root = buildViaRTTI(rtti);
22 | linkViaRTTI(rtti, this, root);
23 | if (root != null) {
24 | root.registerEvent(UIEvent.READY, (_) -> {
25 | onReady();
26 | });
27 | add(root);
28 | }
29 | }
30 |
31 | private function onReady() {
32 | }
33 |
34 | public var component(get, set):Component;
35 | private function get_component():Component {
36 | return root;
37 | }
38 | private function set_component(value:Component):Component {
39 | root = value;
40 | var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this));
41 | linkViaRTTI(rtti, this, root);
42 | return value;
43 | }
44 |
45 | /////////////////////////////////////////////////////////////////////////////////////////////////
46 | // util functions
47 | /////////////////////////////////////////////////////////////////////////////////////////////////
48 | public function addComponent(child:Component):Component {
49 | if (root == null) {
50 | throw "no root component";
51 | }
52 |
53 | return root.addComponent(child);
54 | }
55 |
56 | public function removeComponent(child:Component):Component {
57 | if (root == null) {
58 | throw "no root component";
59 | }
60 |
61 | return root.removeComponent(child);
62 | }
63 |
64 | public function findComponent(criteria:String = null, type:Class = null, recursive:Null = null, searchType:String = "id"):Null {
65 | if (root == null) {
66 | throw "no root component";
67 | }
68 |
69 | return root.findComponent(criteria, type, recursive, searchType);
70 | }
71 |
72 | public function findComponents(styleName:String = null, type:Class = null, maxDepth:Int = 5):Array {
73 | if (root == null) {
74 | throw "no root component";
75 | }
76 |
77 | return root.findComponents(styleName, type, maxDepth);
78 | }
79 |
80 | public function findAncestor(criteria:String = null, type:Class = null, searchType:String = "id"):Null {
81 | if (root == null) {
82 | throw "no root component";
83 | }
84 |
85 | return root.findAncestor(criteria, type, searchType);
86 | }
87 |
88 | public function findComponentsUnderPoint(screenX:Float, screenY:Float, type:Class = null):Array {
89 | if (root == null) {
90 | throw "no root component";
91 | }
92 |
93 | return root.findComponentsUnderPoint(screenX, screenY, type);
94 | }
95 |
96 | public function dispatch(event:T) {
97 | if (root == null) {
98 | throw "no root component";
99 | }
100 |
101 | root.dispatch(event);
102 | }
103 |
104 | public function registerEvent(type:EventType, listener:T->Void, priority:Int = 0) {
105 | if (root == null) {
106 | throw "no root component";
107 | }
108 |
109 | root.registerEvent(type, listener, priority);
110 | }
111 |
112 | public function show() {
113 | if (root == null) {
114 | throw "no root component";
115 | }
116 | root.show();
117 | }
118 |
119 | public function hide() {
120 | if (root == null) {
121 | throw "no root component";
122 | }
123 | root.hide();
124 | }
125 |
126 | public override function destroy() {
127 | super.destroy();
128 | root = null;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UIRuntimeState.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import haxe.ui.core.Component;
4 | import haxe.ui.core.Screen;
5 | import haxe.ui.events.UIEvent;
6 | import haxe.ui.events.EventType;
7 | import haxe.ui.backend.flixel.UIRTTITools.*;
8 |
9 | using StringTools;
10 |
11 | @:rtti
12 | class UIRuntimeState extends UIStateBase { // uses rtti to "build" a class with a similar experience to using macros
13 | public var root:Component;
14 |
15 | public function new() {
16 | super();
17 | }
18 |
19 | public override function create() {
20 | var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this));
21 | root = buildViaRTTI(rtti);
22 | linkViaRTTI(rtti, this, root);
23 | if (root != null) {
24 | root.registerEvent(UIEvent.READY, (_) -> {
25 | onReady();
26 | });
27 | Screen.instance.addComponent(root);
28 | }
29 | super.create();
30 | }
31 |
32 | private function onReady() {
33 | }
34 |
35 | /////////////////////////////////////////////////////////////////////////////////////////////////
36 | // util functions
37 | /////////////////////////////////////////////////////////////////////////////////////////////////
38 | public function addComponent(child:Component):Component {
39 | if (root == null) {
40 | throw "no root component";
41 | }
42 |
43 | return root.addComponent(child);
44 | }
45 |
46 | public function removeComponent(child:Component):Component {
47 | if (root == null) {
48 | throw "no root component";
49 | }
50 |
51 | return root.removeComponent(child);
52 | }
53 |
54 | public function findComponent(criteria:String = null, type:Class = null, recursive:Null = null, searchType:String = "id"):Null {
55 | if (root == null) {
56 | throw "no root component";
57 | }
58 |
59 | return root.findComponent(criteria, type, recursive, searchType);
60 | }
61 |
62 | public function findComponents(styleName:String = null, type:Class = null, maxDepth:Int = 5):Array {
63 | if (root == null) {
64 | throw "no root component";
65 | }
66 |
67 | return root.findComponents(styleName, type, maxDepth);
68 | }
69 |
70 | public function findAncestor(criteria:String = null, type:Class = null, searchType:String = "id"):Null {
71 | if (root == null) {
72 | throw "no root component";
73 | }
74 |
75 | return root.findAncestor(criteria, type, searchType);
76 | }
77 |
78 | public function findComponentsUnderPoint(screenX:Float, screenY:Float, type:Class = null):Array {
79 | if (root == null) {
80 | throw "no root component";
81 | }
82 |
83 | return root.findComponentsUnderPoint(screenX, screenY, type);
84 | }
85 |
86 | public function dispatch(event:T) {
87 | if (root == null) {
88 | throw "no root component";
89 | }
90 |
91 | root.dispatch(event);
92 | }
93 |
94 | public function registerEvent(type:EventType, listener:T->Void, priority:Int = 0) {
95 | if (root == null) {
96 | throw "no root component";
97 | }
98 |
99 | root.registerEvent(type, listener, priority);
100 | }
101 |
102 | public function show() {
103 | if (root == null) {
104 | throw "no root component";
105 | }
106 | root.show();
107 | }
108 |
109 | public function hide() {
110 | if (root == null) {
111 | throw "no root component";
112 | }
113 | root.hide();
114 | }
115 |
116 | public override function destroy() {
117 | if (root != null) {
118 | Screen.instance.removeComponent(root);
119 | }
120 | super.destroy();
121 | root = null;
122 | }
123 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UIRuntimeSubState.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import haxe.ui.core.Component;
4 | import haxe.ui.core.Screen;
5 | import haxe.ui.events.UIEvent;
6 | import haxe.ui.events.EventType;
7 | import haxe.ui.backend.flixel.UIRTTITools.*;
8 |
9 | using StringTools;
10 |
11 | @:rtti
12 | class UIRuntimeSubState extends UISubStateBase { // uses rtti to "build" a class with a similar experience to using macros
13 | public var root:Component;
14 |
15 | public function new() {
16 | super();
17 | }
18 |
19 | public override function create() {
20 | var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this));
21 | root = buildViaRTTI(rtti);
22 | linkViaRTTI(rtti, this, root);
23 | if (root != null) {
24 | root.registerEvent(UIEvent.READY, (_) -> {
25 | onReady();
26 | });
27 | Screen.instance.addComponent(root);
28 | }
29 | super.create();
30 | }
31 |
32 | private function onReady() {
33 | }
34 |
35 | /////////////////////////////////////////////////////////////////////////////////////////////////
36 | // util functions
37 | /////////////////////////////////////////////////////////////////////////////////////////////////
38 | public function addComponent(child:Component):Component {
39 | if (root == null) {
40 | throw "no root component";
41 | }
42 |
43 | return root.addComponent(child);
44 | }
45 |
46 | public function removeComponent(child:Component):Component {
47 | if (root == null) {
48 | throw "no root component";
49 | }
50 |
51 | return root.removeComponent(child);
52 | }
53 |
54 | public function findComponent(criteria:String = null, type:Class = null, recursive:Null = null, searchType:String = "id"):Null {
55 | if (root == null) {
56 | throw "no root component";
57 | }
58 |
59 | return root.findComponent(criteria, type, recursive, searchType);
60 | }
61 |
62 | public function findComponents(styleName:String = null, type:Class = null, maxDepth:Int = 5):Array {
63 | if (root == null) {
64 | throw "no root component";
65 | }
66 |
67 | return root.findComponents(styleName, type, maxDepth);
68 | }
69 |
70 | public function findAncestor(criteria:String = null, type:Class = null, searchType:String = "id"):Null {
71 | if (root == null) {
72 | throw "no root component";
73 | }
74 |
75 | return root.findAncestor(criteria, type, searchType);
76 | }
77 |
78 | public function findComponentsUnderPoint(screenX:Float, screenY:Float, type:Class = null):Array {
79 | if (root == null) {
80 | throw "no root component";
81 | }
82 |
83 | return root.findComponentsUnderPoint(screenX, screenY, type);
84 | }
85 |
86 | public function dispatch(event:T) {
87 | if (root == null) {
88 | throw "no root component";
89 | }
90 |
91 | root.dispatch(event);
92 | }
93 |
94 | public function registerEvent(type:EventType, listener:T->Void, priority:Int = 0) {
95 | if (root == null) {
96 | throw "no root component";
97 | }
98 |
99 | root.registerEvent(type, listener, priority);
100 | }
101 |
102 | public function show() {
103 | if (root == null) {
104 | throw "no root component";
105 | }
106 | root.show();
107 | }
108 |
109 | public function hide() {
110 | if (root == null) {
111 | throw "no root component";
112 | }
113 | root.hide();
114 | }
115 |
116 | public override function destroy() {
117 | if (root != null) {
118 | Screen.instance.removeComponent(root);
119 | }
120 | super.destroy();
121 | root = null;
122 | }
123 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UIState.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import haxe.ui.containers.Box;
4 | import haxe.ui.core.Component;
5 | import haxe.ui.events.UIEvent;
6 | import haxe.ui.layouts.LayoutFactory;
7 |
8 | @:autoBuild(haxe.ui.macros.Macros.buildBehaviours())
9 | @:autoBuild(haxe.ui.macros.Macros.build())
10 | @:autoBuild(haxe.ui.backend.flixel.macros.UIStateMacro.checkDefine())
11 | class UIState extends UIStateBase { // must use -D haxeui_dont_impose_base_class
12 | public var bindingRoot:Bool = false;
13 |
14 | private var root:Box = new Box(); // root component is always a box for now, since there is no nice / easy way to get the root node info from the macro
15 |
16 | public function new() {
17 | super();
18 | }
19 |
20 | private function applyRootLayout(l:String) {
21 | if (l == "vbox") {
22 | root.layout = LayoutFactory.createFromName("vertical");
23 | } else if (l == "hbox") {
24 | root.layout = LayoutFactory.createFromName("horizontal");
25 | }
26 | }
27 |
28 | public override function create() {
29 | super.create();
30 | root.registerEvent(UIEvent.READY, (_) -> {
31 | onReady();
32 | });
33 | add(root);
34 | }
35 |
36 | private function onReady() {
37 | }
38 |
39 | public function validateNow() {
40 | root.validateNow();
41 | }
42 |
43 | private function registerBehaviours() {
44 | }
45 |
46 | public function addComponent(child:Component):Component {
47 | return root.addComponent(child);
48 | }
49 |
50 | public var width(get, set):Float;
51 | private function get_width():Float {
52 | return root.width;
53 | }
54 | private function set_width(value:Float):Float {
55 | root.width = value;
56 | return value;
57 | }
58 |
59 | public var percentWidth(get, set):Float;
60 | private function get_percentWidth():Float {
61 | return root.percentWidth;
62 | }
63 | private function set_percentWidth(value:Float):Float {
64 | root.percentWidth = value;
65 | return value;
66 | }
67 |
68 | public var height(get, set):Float;
69 | private function get_height():Float {
70 | return root.height ;
71 | }
72 | private function set_height(value:Float):Float {
73 | root.height = value;
74 | return value;
75 | }
76 |
77 | public var percentHeight(get, set):Float;
78 | private function get_percentHeight():Float {
79 | return root.percentHeight ;
80 | }
81 | private function set_percentHeight(value:Float):Float {
82 | root.percentHeight = value;
83 | return value;
84 | }
85 |
86 | public var styleString(get, set):String;
87 | private function get_styleString():String {
88 | return root.styleString ;
89 | }
90 | private function set_styleString(value:String):String {
91 | root.styleString = value;
92 | return value;
93 | }
94 |
95 | public function show() {
96 | root.show();
97 | }
98 |
99 | public function hide() {
100 | root.hide();
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UIStateBase.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | typedef UIStateBase = flixel.FlxState;
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UISubState.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | import haxe.ui.layouts.LayoutFactory;
4 | import haxe.ui.containers.Box;
5 | import haxe.ui.core.Component;
6 | import haxe.ui.events.UIEvent;
7 |
8 | @:autoBuild(haxe.ui.macros.Macros.buildBehaviours())
9 | @:autoBuild(haxe.ui.macros.Macros.build())
10 | @:autoBuild(haxe.ui.backend.flixel.macros.UIStateMacro.checkDefine())
11 | class UISubState extends UISubStateBase { // must use -D haxeui_dont_impose_base_class
12 | public var bindingRoot:Bool = false;
13 |
14 | private var root:Box = new Box(); // root component is always a box for now, since there is no nice / easy way to get the root node info from the macro
15 |
16 | public function new() {
17 | super();
18 | }
19 |
20 | private function applyRootLayout(l:String) {
21 | if (l == "vbox") {
22 | root.layout = LayoutFactory.createFromName("vertical");
23 | } else if (l == "hbox") {
24 | root.layout = LayoutFactory.createFromName("horizontal");
25 | }
26 | }
27 |
28 | public override function create() {
29 | super.create();
30 | root.registerEvent(UIEvent.READY, (_) -> {
31 | onReady();
32 | });
33 | add(root);
34 | }
35 |
36 | private function onReady() {
37 | }
38 |
39 | public function validateNow() {
40 | root.validateNow();
41 | }
42 |
43 | private function registerBehaviours() {
44 | }
45 |
46 | public function addComponent(child:Component):Component {
47 | return root.addComponent(child);
48 | }
49 |
50 | public var width(get, set):Float;
51 | private function get_width():Float {
52 | return root.width;
53 | }
54 | private function set_width(value:Float):Float {
55 | root.width = value;
56 | return value;
57 | }
58 |
59 | public var percentWidth(get, set):Float;
60 | private function get_percentWidth():Float {
61 | return root.percentWidth;
62 | }
63 | private function set_percentWidth(value:Float):Float {
64 | root.percentWidth = value;
65 | return value;
66 | }
67 |
68 | public var height(get, set):Float;
69 | private function get_height():Float {
70 | return root.height ;
71 | }
72 | private function set_height(value:Float):Float {
73 | root.height = value;
74 | return value;
75 | }
76 |
77 | public var percentHeight(get, set):Float;
78 | private function get_percentHeight():Float {
79 | return root.percentHeight ;
80 | }
81 | private function set_percentHeight(value:Float):Float {
82 | root.percentHeight = value;
83 | return value;
84 | }
85 |
86 | public var styleString(get, set):String;
87 | private function get_styleString():String {
88 | return root.styleString ;
89 | }
90 | private function set_styleString(value:String):String {
91 | root.styleString = value;
92 | return value;
93 | }
94 |
95 | public function show() {
96 | root.show();
97 | }
98 |
99 | public function hide() {
100 | root.hide();
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/UISubStateBase.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel;
2 |
3 | typedef UISubStateBase = flixel.FlxSubState;
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/_module/styles/default/cursors/pointer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haxeui/haxeui-flixel/100f2c96beab619cfe72c567a058c41c71e3e998/haxe/ui/backend/flixel/_module/styles/default/cursors/pointer.png
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/_module/styles/default/main.css:
--------------------------------------------------------------------------------
1 | .label {
2 | font-name: "flixel/fonts/nokiafc22.ttf";
3 | font-size: 8px;
4 | }
5 |
6 | .textfield, .textarea {
7 | font-name: "flixel/fonts/nokiafc22.ttf";
8 | font-size: 8px;
9 | }
10 |
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/components/SparrowPlayer.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel.components;
2 |
3 | import flixel.FlxSprite;
4 | import flixel.graphics.frames.FlxAtlasFrames;
5 | import flixel.graphics.frames.FlxFramesCollection;
6 | import haxe.ui.containers.Box;
7 | import haxe.ui.core.Component;
8 | import haxe.ui.core.IDataComponent;
9 | import haxe.ui.data.DataSource;
10 | import haxe.ui.events.AnimationEvent;
11 | import haxe.ui.geom.Size;
12 | import haxe.ui.layouts.DefaultLayout;
13 | import openfl.Assets;
14 |
15 | private typedef AnimationInfo = {
16 | var name:String;
17 | var prefix:String;
18 | var frameRate:Null; // default 30
19 | var looped:Null; // default true
20 | var flipX:Null; // default false
21 | var flipY:Null; // default false
22 | }
23 |
24 | /*
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | */
34 |
35 | @:composite(Layout)
36 | class SparrowPlayer extends Box implements IDataComponent {
37 | private var sprite:FlxSprite;
38 |
39 | public function new() {
40 | super();
41 | sprite = new FlxSprite(1, 1);
42 | add(sprite);
43 | }
44 |
45 | private var _xmlFile:String;
46 | public var xmlFile(get, set):String;
47 | private function get_xmlFile():String {
48 | return _xmlFile;
49 | }
50 | private function set_xmlFile(value:String):String {
51 | _xmlFile = value;
52 | loadAnimation(_xmlFile, _pngFile);
53 | return value;
54 | }
55 |
56 | private var _pngFile:String;
57 | public var pngFile(get, set):String;
58 | private function get_pngFile():String {
59 | return _pngFile;
60 | }
61 | private function set_pngFile(value:String):String {
62 | _pngFile = value;
63 | loadAnimation(_xmlFile, _pngFile);
64 | return value;
65 | }
66 |
67 | private var _dataSource:DataSource = null;
68 | private override function get_dataSource():DataSource {
69 | return _dataSource;
70 | }
71 | private override function set_dataSource(value:DataSource):DataSource {
72 | _dataSource = value;
73 | for (i in 0..._dataSource.size) {
74 | var item:Dynamic = _dataSource.get(i);
75 | if (item.frameRate == null) {
76 | item.frameRate = 30;
77 | }
78 | if (item.looped == null) {
79 | item.looped = true;
80 | }
81 | if (item.flipX == null) {
82 | item.flipX = false;
83 | }
84 | if (item.flipY == null) {
85 | item.flipY = false;
86 | }
87 | addAnimationByPrefix(item.name, item.prefix, Std.parseInt(item.frameRate), Std.string(item.looped) == "true", Std.string(item.flipX) == "true", Std.string(item.flipY) == "true");
88 | }
89 | return value;
90 | }
91 |
92 | private var _cachedAnimationName:String = null; // component might not have an animation yet, so we'll cache it if thats the case
93 | private var _animationName:String = null;
94 | public var animationName(get, set):String;
95 | private function get_animationName() {
96 | return _animationName;
97 | }
98 | private function set_animationName(value:String):String {
99 | if (!_animationLoaded) {
100 | _cachedAnimationName = value;
101 | return value;
102 | }
103 |
104 | if (sprite.animation.getByName(value) != null) {
105 | _cachedAnimationName = null;
106 | _animationName = value;
107 | sprite.animation.play(_animationName);
108 | invalidateComponentLayout();
109 |
110 | if (hasEvent(AnimationEvent.START)) {
111 | dispatch(new AnimationEvent(AnimationEvent.START));
112 | } else {
113 | _redispatchStart = true;
114 | }
115 | } else {
116 | _cachedAnimationName = value;
117 | }
118 | return value;
119 | }
120 |
121 | private var _redispatchLoaded:Bool = false; // possible haxeui bug: if listener is added after event is dispatched, event is "lost"... needs thinking about, is it smart to "collect and redispatch"? Not sure
122 | private var _redispatchStart:Bool = false; // possible haxeui bug: if listener is added after event is dispatched, event is "lost"... needs thinking about, is it smart to "collect and redispatch"? Not sure
123 | public override function onReady() {
124 | super.onReady();
125 | if (_cachedAnimationName != null) {
126 | animationName = _cachedAnimationName;
127 | }
128 | invalidateComponentLayout();
129 |
130 | if (_redispatchLoaded) {
131 | _redispatchLoaded = false;
132 | dispatch(new AnimationEvent(AnimationEvent.LOADED));
133 | }
134 |
135 | if (_redispatchStart) {
136 | _redispatchStart = false;
137 | dispatch(new AnimationEvent(AnimationEvent.START));
138 | }
139 | }
140 |
141 | private var _cachedAnimationPrefixes:Array = []; // component might not have an animation yet, so we'll cache it if thats the case
142 | public function addAnimationByPrefix(name:String, prefix:String, frameRate:Int = 30, looped:Bool = true, flipX:Bool = false, flipY:Bool = false) {
143 | if (!_animationLoaded) {
144 | if (_cachedAnimationPrefixes == null) {
145 | _cachedAnimationPrefixes = [];
146 | }
147 | _cachedAnimationPrefixes.push({
148 | name: name,
149 | prefix: prefix,
150 | frameRate: frameRate,
151 | looped: looped,
152 | flipX: flipX,
153 | flipY: flipY
154 | });
155 | return;
156 | }
157 |
158 | sprite.animation.addByPrefix(name, prefix, frameRate, looped, flipX, flipY);
159 | if (_cachedAnimationName != null) {
160 | animationName = _cachedAnimationName;
161 | }
162 | }
163 |
164 | private var _animationLoaded:Bool = false;
165 | public function loadAnimation(xml:String, png:String) {
166 | if (xml == null || png == null) {
167 | return;
168 | }
169 |
170 | var frames:FlxFramesCollection = FlxAtlasFrames.fromSparrow(png, Assets.getText(xml));
171 | sprite.frames = frames;
172 | #if (flixel >= "5.9.0")
173 | if (!sprite.animation.onFrameChange.has(onFrame)) {
174 | sprite.animation.onFrameChange.add(onFrame);
175 | }
176 | if (!sprite.animation.onFinish.has(onFinish)) {
177 | sprite.animation.onFinish.add(onFinish);
178 | }
179 | #else
180 | if (sprite.animation.callback == null) {
181 | sprite.animation.callback = onFrame;
182 | }
183 | if (sprite.animation.finishCallback == null) {
184 | sprite.animation.finishCallback = onFinish;
185 | }
186 | #end
187 | invalidateComponentLayout();
188 | _animationLoaded = true;
189 |
190 | if (_cachedAnimationPrefixes != null) {
191 | while (_cachedAnimationPrefixes.length > 0) {
192 | var item = _cachedAnimationPrefixes.shift();
193 | addAnimationByPrefix(item.name, item.prefix, item.frameRate, item.looped, item.flipX, item.flipY);
194 | }
195 | _cachedAnimationPrefixes = null;
196 | }
197 |
198 | if (_cachedAnimationName != null) {
199 | animationName = _cachedAnimationName;
200 | }
201 |
202 | if (hasEvent(AnimationEvent.LOADED)) {
203 | dispatch(new AnimationEvent(AnimationEvent.LOADED));
204 | } else {
205 | _redispatchLoaded = true;
206 | }
207 | }
208 |
209 | public var currentFrameCount(get, null):Int;
210 | private function get_currentFrameCount() {
211 | if (sprite.animation == null || sprite.animation.curAnim == null) {
212 | return 0;
213 | }
214 |
215 | return sprite.animation.curAnim.numFrames;
216 | }
217 |
218 | public var currentFrameNumber(get, null):Int;
219 | private function get_currentFrameNumber() {
220 | if (sprite.animation == null || sprite.animation.curAnim == null) {
221 | return 0;
222 | }
223 |
224 | return sprite.animation.curAnim.curFrame;
225 | }
226 |
227 | public var frameNames(get, null):Array;
228 | private function get_frameNames():Array {
229 | if (sprite.animation == null) {
230 | return [];
231 | }
232 |
233 | return sprite.animation.getNameList();
234 | }
235 |
236 | private function onFrame(name:String, frameNumber:Int, frameIndex:Int) {
237 | dispatch(new AnimationEvent(AnimationEvent.FRAME));
238 | }
239 |
240 | private function onFinish(name:String) {
241 | dispatch(new AnimationEvent(AnimationEvent.END));
242 | }
243 |
244 | /*
245 | // lets override a few flixel / haxeui-flixel specifics to get things nice and smooth
246 | public override function update(elapsed:Float) {
247 | super.update(elapsed);
248 | // these lines make the clipping _much_ better but im not sure if its smart
249 | var cc = findClipComponent();
250 | if (cc != null) {
251 | var cr = cc.componentClipRect;
252 | var rc = FlxRect.get(screenLeft + cr.left, screenTop + cr.top, cr.width, cr.height);
253 | this.clipRect = rc;
254 | rc.put();
255 | }
256 | }
257 | */
258 |
259 | private override function repositionChildren() {
260 | super.repositionChildren();
261 | sprite.x = this.cachedScreenX;
262 | sprite.y = this.cachedScreenY;
263 | }
264 | }
265 |
266 | @:access(haxe.ui.backend.flixel.components.SparrowPlayer)
267 | private class Layout extends DefaultLayout {
268 | public override function resizeChildren() {
269 | super.resizeChildren();
270 |
271 | var player = cast(_component, SparrowPlayer);
272 | var sprite = player.sprite;
273 | if (sprite == null) {
274 | return super.resizeChildren();
275 | }
276 |
277 | sprite.origin.set(0, 0);
278 | sprite.setGraphicSize(Std.int(innerWidth), Std.int(innerHeight));
279 | }
280 |
281 | public override function calcAutoSize(exclusions:Array = null):Size {
282 | var player = cast(_component, SparrowPlayer);
283 | var sprite = player.sprite;
284 | if (sprite == null) {
285 | return super.calcAutoSize(exclusions);
286 | }
287 | var size = new Size();
288 | size.width = sprite.frameWidth + paddingLeft + paddingRight;
289 | size.height = sprite.frameHeight + paddingTop + paddingBottom;
290 | return size;
291 | }
292 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/components/SpriteWrapper.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel.components;
2 |
3 | import flixel.FlxSprite;
4 | import flixel.math.FlxRect;
5 | import haxe.ui.containers.Box;
6 | import haxe.ui.core.Component;
7 | import haxe.ui.geom.Size;
8 | import haxe.ui.layouts.DefaultLayout;
9 |
10 | @:composite(Layout)
11 | class SpriteWrapper extends Box {
12 | public var spriteOffsetX:Float = 0;
13 | public var spriteOffsetY:Float = 0;
14 |
15 | private var _sprite:FlxSprite = null;
16 | public var sprite(get, set):FlxSprite;
17 | private function get_sprite():FlxSprite {
18 | return _sprite;
19 | }
20 | private function set_sprite(value:FlxSprite):FlxSprite {
21 | if (_sprite != null) {
22 | remove(_sprite);
23 | }
24 | _sprite = value;
25 | add(_sprite);
26 | invalidateComponentLayout();
27 | return value;
28 | }
29 |
30 | private override function repositionChildren() {
31 | super.repositionChildren();
32 | if (sprite != null) {
33 | sprite.x = spriteOffsetX + this.cachedScreenX;
34 | sprite.y = spriteOffsetY + this.cachedScreenY;
35 | }
36 | }
37 | }
38 |
39 | @:access(haxe.ui.backend.flixel.components.SpriteWrapper)
40 | private class Layout extends DefaultLayout {
41 | public override function resizeChildren() {
42 | super.resizeChildren();
43 |
44 | var wrapper = cast(_component, SpriteWrapper);
45 | var sprite = wrapper.sprite;
46 | if (sprite == null) {
47 | return super.resizeChildren();
48 | }
49 |
50 | sprite.origin.set(0, 0);
51 | sprite.setGraphicSize(Std.int(innerWidth), Std.int(innerHeight));
52 | }
53 |
54 | public override function calcAutoSize(exclusions:Array = null):Size {
55 | var wrapper = cast(_component, SpriteWrapper);
56 | var sprite = wrapper.sprite;
57 | if (sprite == null) {
58 | return super.calcAutoSize(exclusions);
59 | }
60 | var size = new Size();
61 | size.width = sprite.width + paddingLeft + paddingRight;
62 | size.height = sprite.height + paddingTop + paddingBottom;
63 | return size;
64 | }
65 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/macros/UIStateMacro.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel.macros;
2 |
3 | #if macro
4 | import haxe.macro.Expr.Field;
5 | import haxe.macro.Context;
6 |
7 | /**
8 | * Macro which makes an error at compile time if the user attemps to use `UIState` or `UISubState` without having `haxeui_dont_impose_base_class` defined.
9 | */
10 | class UIStateMacro {
11 | public static function checkDefine():Array {
12 | var localClass:String = Context.getLocalClass().get().name;
13 |
14 | if (!Context.defined("haxeui_dont_impose_base_class"))
15 | Context.error("You must define haxeui_dont_impose_base_class in order to use " + findUIClass() + " (for class " + localClass + ")", Context.currentPos());
16 |
17 | return Context.getBuildFields();
18 | }
19 |
20 | static function findUIClass():String {
21 | var cls = Context.getLocalClass().get();
22 |
23 | while (!isUIState(cls.name)) {
24 | if (cls.superClass == null)
25 | break;
26 |
27 | cls = cls.superClass.t.get();
28 | }
29 |
30 | if (!isUIState(cls.name)) {
31 | // shouldn't happen, but just in case
32 | return "UI states";
33 | }
34 |
35 | return cls.name;
36 | }
37 |
38 | static function isUIState(name:String):Bool {
39 | return name == "UIState" || name == "UISubState";
40 | }
41 | }
42 | #end
43 |
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/textinputs/FlxTextInput.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel.textinputs;
2 |
3 | import flixel.FlxSprite;
4 | import flixel.text.FlxInputText;
5 | import flixel.util.FlxColor;
6 | import haxe.ui.backend.TextInputImpl.TextInputEvent;
7 | import haxe.ui.core.Component;
8 | import openfl.events.Event;
9 | import openfl.events.KeyboardEvent;
10 |
11 | #if (flixel >= "5.9.0")
12 | class FlxTextInput extends TextBase {
13 | public static var USE_ON_ADDED:Bool = false;
14 | public static var USE_ON_REMOVED:Bool = false;
15 |
16 | private static inline var PADDING_X:Int = 4;
17 | private static inline var PADDING_Y:Int = 2;
18 |
19 | private var tf:FlxInputText;
20 |
21 | public function new() {
22 | super();
23 | tf = new FlxInputText(0, 0, 0, null, 8, FlxColor.BLACK, FlxColor.TRANSPARENT);
24 | tf.onTextChange.add(onInternalChange);
25 | tf.onScrollChange.add(onScroll);
26 | tf.pixelPerfectRender = true;
27 | _inputData.vscrollPageStep = 1;
28 | _inputData.vscrollNativeWheel = true;
29 | }
30 |
31 | public override function focus() {
32 | tf.startFocus();
33 | }
34 |
35 | public override function blur() {
36 | tf.endFocus();
37 | }
38 |
39 | public function attach() {
40 | }
41 |
42 | public var visible(get, set):Bool;
43 | private function get_visible():Bool {
44 | return tf.visible;
45 | }
46 | private function set_visible(value:Bool):Bool {
47 | tf.active = tf.visible = value; // text input shouldn't be active if it's hidden
48 | return value;
49 | }
50 |
51 | public var x(get, set):Float;
52 | private function get_x():Float {
53 | return tf.x;
54 | }
55 | private function set_x(value:Float):Float {
56 | tf.x = value;
57 | return value;
58 | }
59 |
60 | public var y(get, set):Float;
61 | private function get_y():Float {
62 | return tf.y;
63 | }
64 | private function set_y(value:Float):Float {
65 | tf.y = value;
66 | return value;
67 | }
68 |
69 | public var scaleX(get, set):Float;
70 | private function get_scaleX():Float {
71 | return tf.scale.x;
72 | }
73 | private function set_scaleX(value:Float):Float {
74 | // do nothing
75 | return value;
76 | }
77 |
78 | public var scaleY(get, set):Float;
79 | private function get_scaleY():Float {
80 | return tf.scale.y;
81 | }
82 | private function set_scaleY(value:Float):Float {
83 | // do nothing
84 | return value;
85 | }
86 |
87 | public var alpha(get, set):Float;
88 | private function get_alpha():Float {
89 | return tf.alpha;
90 | }
91 | private function set_alpha(value:Float):Float {
92 | tf.alpha = value;
93 | return value;
94 | }
95 |
96 | private override function validateData() {
97 | if (_text != null) {
98 | if (_dataSource == null) {
99 | tf.text = normalizeText(_text);
100 | }
101 | }
102 |
103 | var hscrollValue = Std.int(_inputData.hscrollPos);
104 | if (tf.scrollH != hscrollValue) {
105 | tf.scrollH = hscrollValue;
106 | }
107 |
108 | var vscrollValue = Std.int(_inputData.vscrollPos) + 1;
109 | if (tf.scrollV != vscrollValue) {
110 | tf.scrollV = vscrollValue;
111 | }
112 | }
113 |
114 | private function normalizeText(text:String):String {
115 | text = StringTools.replace(text, "\\n", "\n");
116 | return text;
117 | }
118 |
119 | private override function measureText() {
120 | //tf.width = _width * Toolkit.scaleX;
121 | _textHeight = tf.textField.textHeight;
122 | if (_textHeight == 0) {
123 | var tmpText:String = tf.text;
124 | tf.text = "|";
125 | _textHeight = tf.textField.textHeight;
126 | tf.text = tmpText;
127 | }
128 |
129 | _textWidth = Math.round(_textWidth) / Toolkit.scaleX;
130 | _textHeight = Math.round(_textHeight) / Toolkit.scaleY;
131 |
132 | //////////////////////////////////////////////////////////////////////////////
133 |
134 | _inputData.hscrollMax = tf.maxScrollH;
135 | // see below
136 | _inputData.hscrollPageSize = (_width * _inputData.hscrollMax) / _textWidth;
137 |
138 | _inputData.vscrollMax = tf.maxScrollV - 1;
139 | _inputData.vscrollPageSize = (_height * _inputData.vscrollMax) / _textHeight;
140 | }
141 |
142 | private override function validateStyle():Bool {
143 | var measureTextRequired:Bool = false;
144 |
145 | if (_textStyle != null) {
146 | var textAlign = (_textStyle.textAlign != null ? _textStyle.textAlign : "left");
147 | if (tf.alignment != textAlign) {
148 | tf.alignment = textAlign;
149 | }
150 |
151 | var fontSizeValue = Std.int(_textStyle.fontSize);
152 | if (tf.size != fontSizeValue) {
153 | tf.size = Std.int(fontSizeValue * Toolkit.scale);
154 |
155 | measureTextRequired = true;
156 | }
157 |
158 | if (_fontInfo != null && tf.font != _fontInfo.data) {
159 | tf.font = _fontInfo.data;
160 | measureTextRequired = true;
161 | }
162 |
163 | if (tf.color != _textStyle.color) {
164 | tf.color = _textStyle.color;
165 | }
166 |
167 | var fontBold = (_textStyle.fontBold != null ? _textStyle.fontBold : false);
168 | if (tf.bold != fontBold) {
169 | tf.bold = fontBold;
170 | measureTextRequired = true;
171 | }
172 |
173 | var fontItalic = (_textStyle.fontItalic != null ? _textStyle.fontItalic : false);
174 | if (tf.italic != fontItalic) {
175 | tf.italic = fontItalic;
176 | measureTextRequired = true;
177 | }
178 | }
179 |
180 | if (tf.wordWrap != _displayData.wordWrap) {
181 | tf.wordWrap = _displayData.wordWrap;
182 | measureTextRequired = true;
183 | }
184 |
185 | if (tf.multiline != _displayData.multiline) {
186 | tf.multiline = _displayData.multiline;
187 | // `multiline` only decides whether the user can add new lines,
188 | // so measuring the text is not required.
189 | }
190 |
191 | if (tf.passwordMode != _inputData.password) {
192 | tf.passwordMode = _inputData.password;
193 | measureTextRequired = true;
194 | }
195 |
196 | tf.editable = !parentComponent.disabled;
197 |
198 | return measureTextRequired;
199 | }
200 |
201 | private override function validatePosition() {
202 | _left = Math.round(_left * Toolkit.scaleX);
203 | _top = Math.round(_top * Toolkit.scaleY);
204 | }
205 |
206 | private override function validateDisplay() {
207 | if (_width <= 0 || _height <= 0) {
208 | return;
209 | }
210 |
211 | if (tf.width != _width * Toolkit.scaleX) {
212 | tf.width = _width * Toolkit.scaleX;
213 | tf.fieldWidth = tf.width;
214 | }
215 |
216 | if (tf.height != (_height + PADDING_Y) * Toolkit.scaleY) {
217 | tf.height = (_height + PADDING_Y) * Toolkit.scaleY;
218 | tf.fieldHeight = tf.height;
219 | }
220 | }
221 |
222 | private var _onMouseDown:TextInputEvent->Void = null;
223 | public var onMouseDown(null, set):TextInputEvent->Void;
224 | private function set_onMouseDown(value:TextInputEvent->Void):TextInputEvent->Void {
225 | if (_onMouseDown != null) {
226 | //tf.removeEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseDownEvent);
227 | }
228 | _onMouseDown = value;
229 | if (_onMouseDown != null) {
230 | //tf.addEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseDownEvent);
231 | }
232 | return value;
233 | }
234 |
235 | /*
236 | private function __onTextInputMouseDownEvent(event:openfl.events.MouseEvent) {
237 | if (_onMouseDown != null) {
238 | _onMouseDown({
239 | type: event.type,
240 | stageX: event.stageX,
241 | stageY: event.stageY
242 | });
243 | }
244 | }
245 | */
246 |
247 | private var _onMouseUp:TextInputEvent->Void = null;
248 | public var onMouseUp(null, set):TextInputEvent->Void;
249 | private function set_onMouseUp(value:TextInputEvent->Void):TextInputEvent->Void {
250 | if (_onMouseUp != null) {
251 | //tf.removeEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseUpEvent);
252 | }
253 | _onMouseUp = value;
254 | if (_onMouseUp != null) {
255 | //tf.addEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseUpEvent);
256 | }
257 | return value;
258 | }
259 |
260 | /*
261 | private function __onTextInputMouseUpEvent(event:openfl.events.MouseEvent) {
262 | if (_onMouseUp != null) {
263 | _onMouseUp({
264 | type: event.type,
265 | stageX: event.stageX,
266 | stageY: event.stageY
267 | });
268 | }
269 | }
270 | */
271 |
272 | public function equals(sprite:FlxSprite):Bool {
273 | return sprite == tf;
274 | }
275 |
276 | private var _onClick:TextInputEvent->Void = null;
277 | public var onClick(null, set):TextInputEvent->Void;
278 | private function set_onClick(value:TextInputEvent->Void):TextInputEvent->Void {
279 | if (_onClick != null) {
280 | //tf.removeEventListener(openfl.events.MouseEvent.CLICK, __onTextInputClickEvent);
281 | }
282 | _onClick = value;
283 | if (_onClick != null) {
284 | //tf.addEventListener(openfl.events.MouseEvent.CLICK, __onTextInputClickEvent);
285 | }
286 | return value;
287 | }
288 |
289 | /*
290 | private function __onTextInputClickEvent(event:openfl.events.MouseEvent) {
291 | if (_onClick != null) {
292 | _onClick({
293 | type: event.type,
294 | stageX: event.stageX,
295 | stageY: event.stageY
296 | });
297 | }
298 | }
299 | */
300 |
301 | private var _onChange:TextInputEvent->Void = null;
302 | public var onChange(null, set):TextInputEvent->Void;
303 | private function set_onChange(value:TextInputEvent->Void):TextInputEvent->Void {
304 | if (_onChange != null) {
305 | tf.onTextChange.remove(__onTextInputChangeEvent);
306 | }
307 | _onChange = value;
308 | if (_onChange != null) {
309 | tf.onTextChange.add(__onTextInputChangeEvent);
310 | }
311 | return value;
312 | }
313 |
314 | private function __onTextInputChangeEvent(text:String, action:FlxInputTextChange) {
315 | if (_onChange != null) {
316 | _onChange({
317 | type: "change",
318 | stageX: 0,
319 | stageY: 0
320 | });
321 | }
322 | }
323 |
324 | private var _onKeyDown:KeyboardEvent->Void = null;
325 | public var onKeyDown(null, set):KeyboardEvent->Void;
326 | private function set_onKeyDown(value:KeyboardEvent->Void):KeyboardEvent->Void {
327 | if (_onKeyDown != null) {
328 | //tf.textField.removeEventListener(KeyboardEvent.KEY_DOWN, __onTextInputKeyDown);
329 | }
330 | _onKeyDown = value;
331 | if (_onKeyDown != null) {
332 | //tf.textField.addEventListener(KeyboardEvent.KEY_DOWN, __onTextInputKeyDown);
333 | }
334 | return value;
335 | }
336 |
337 | /*
338 | private function __onTextInputKeyDown(e:KeyboardEvent) {
339 | if (_onKeyDown != null)
340 | _onKeyDown(e);
341 | }
342 | */
343 |
344 | private var _onKeyUp:KeyboardEvent->Void = null;
345 | public var onKeyUp(null, set):KeyboardEvent->Void;
346 | private function set_onKeyUp(value:KeyboardEvent->Void):KeyboardEvent->Void {
347 | if (_onKeyUp != null) {
348 | //tf.textField.removeEventListener(KeyboardEvent.KEY_UP, __onTextInputKeyUp);
349 | }
350 | _onKeyUp = value;
351 | if (_onKeyUp != null) {
352 | //tf.textField.addEventListener(KeyboardEvent.KEY_UP, __onTextInputKeyUp);
353 | }
354 | return value;
355 | }
356 |
357 | /*
358 | private function __onTextInputKeyUp(e:KeyboardEvent) {
359 | if (_onKeyUp != null)
360 | _onKeyUp(e);
361 | }
362 | */
363 |
364 | private function onInternalChange(text:String, action:FlxInputTextChange) {
365 | _text = tf.text;
366 |
367 | measureText();
368 |
369 | if (_inputData.onChangedCallback != null) {
370 | _inputData.onChangedCallback();
371 | }
372 | }
373 |
374 | private function onScroll(scrollH:Int, scrollV:Int) {
375 | _inputData.hscrollPos = tf.scrollH;
376 | _inputData.vscrollPos = tf.scrollV - 1;
377 |
378 | if (_inputData.onScrollCallback != null) {
379 | _inputData.onScrollCallback();
380 | }
381 | }
382 |
383 | public function update() {
384 | }
385 |
386 | public function addToComponent(component:Component) {
387 | //StateHelper.currentState.add(tf);
388 | component.add(tf);
389 | }
390 |
391 | public function destroy(component:Component) {
392 | tf.visible = false;
393 | component.remove(tf, true);
394 | tf.destroy();
395 | tf = null;
396 | }
397 | }
398 | #end
--------------------------------------------------------------------------------
/haxe/ui/backend/flixel/textinputs/OpenFLTextInput.hx:
--------------------------------------------------------------------------------
1 | package haxe.ui.backend.flixel.textinputs;
2 |
3 | import flixel.FlxG;
4 | import flixel.FlxSprite;
5 | import haxe.ui.Toolkit;
6 | import haxe.ui.backend.TextInputImpl.TextInputEvent;
7 | import haxe.ui.core.Component;
8 | import haxe.ui.core.Screen;
9 | import haxe.ui.events.UIEvent;
10 | import haxe.ui.geom.Rectangle;
11 | import openfl.events.Event;
12 | import openfl.events.KeyboardEvent;
13 | import openfl.text.TextField;
14 | import openfl.text.TextFieldAutoSize;
15 | import openfl.text.TextFieldType;
16 | import openfl.text.TextFormat;
17 |
18 | class OpenFLTextInput extends TextBase {
19 | public static var USE_ON_ADDED:Bool = true;
20 | public static var USE_ON_REMOVED:Bool = true;
21 |
22 | private var PADDING_X:Int = 4;
23 | private var PADDING_Y:Int = 0;
24 |
25 | public var tf:TextField;
26 |
27 | public function new() {
28 | super();
29 | tf = new TextField();
30 | tf.type = TextFieldType.INPUT;
31 | tf.selectable = true;
32 | tf.mouseEnabled = true;
33 | tf.autoSize = TextFieldAutoSize.NONE;
34 | tf.multiline = true;
35 | tf.wordWrap = true;
36 | tf.tabEnabled = false;
37 | //tf.stage.focus = null;
38 | tf.addEventListener(Event.CHANGE, onInternalChange);
39 | _inputData.vscrollPageStep = 1;
40 | _inputData.vscrollNativeWheel = true;
41 | }
42 |
43 | public override function focus() {
44 | if (tf.stage != null) {
45 | //tf.stage.focus = tf;
46 | }
47 | }
48 |
49 | public override function blur() {
50 | if (tf.stage != null) {
51 | //tf.stage.focus = null;
52 | }
53 | }
54 |
55 | public function attach() {
56 | }
57 |
58 | public var visible(get, set):Bool;
59 | private function get_visible():Bool {
60 | return tf.visible;
61 | }
62 | private function set_visible(value:Bool):Bool {
63 | tf.visible = value;
64 | return value;
65 | }
66 |
67 | public var x(get, set):Float;
68 | private function get_x():Float {
69 | return tf.x;
70 | }
71 | private function set_x(value:Float):Float {
72 | tf.x = value * FlxG.scaleMode.scale.x;
73 | return value;
74 | }
75 |
76 | public var y(get, set):Float;
77 | private function get_y():Float {
78 | return tf.y;
79 | }
80 | private function set_y(value:Float):Float {
81 | tf.y = value * FlxG.scaleMode.scale.y;
82 | return value;
83 | }
84 |
85 | public var scaleX(get, set):Float;
86 | private function get_scaleX():Float {
87 | return tf.scaleX;
88 | }
89 | private function set_scaleX(value:Float):Float {
90 | tf.scaleX = value;
91 | return value;
92 | }
93 |
94 | public var scaleY(get, set):Float;
95 | private function get_scaleY():Float {
96 | return tf.scaleY;
97 | }
98 | private function set_scaleY(value:Float):Float {
99 | tf.scaleY = value;
100 | return value;
101 | }
102 |
103 | public var alpha(get, set):Float;
104 | private function get_alpha():Float {
105 | return tf.alpha;
106 | }
107 | private function set_alpha(value:Float):Float {
108 | tf.alpha = value;
109 | return value;
110 | }
111 |
112 |
113 | private var _onMouseDown:TextInputEvent->Void = null;
114 | public var onMouseDown(null, set):TextInputEvent->Void;
115 | private function set_onMouseDown(value:TextInputEvent->Void):TextInputEvent->Void {
116 | if (_onMouseDown != null) {
117 | tf.removeEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseDownEvent);
118 | }
119 | _onMouseDown = value;
120 | if (_onMouseDown != null) {
121 | tf.addEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseDownEvent);
122 | }
123 | return value;
124 | }
125 |
126 | private function __onTextInputMouseDownEvent(event:openfl.events.MouseEvent) {
127 | if (_onMouseDown != null) {
128 | _onMouseDown({
129 | type: event.type,
130 | stageX: event.stageX,
131 | stageY: event.stageY
132 | });
133 | }
134 | }
135 |
136 | private var _onMouseUp:TextInputEvent->Void = null;
137 | public var onMouseUp(null, set):TextInputEvent->Void;
138 | private function set_onMouseUp(value:TextInputEvent->Void):TextInputEvent->Void {
139 | if (_onMouseUp != null) {
140 | tf.removeEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseUpEvent);
141 | }
142 | _onMouseUp = value;
143 | if (_onMouseUp != null) {
144 | tf.addEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseUpEvent);
145 | }
146 | return value;
147 | }
148 |
149 | private function __onTextInputMouseUpEvent(event:openfl.events.MouseEvent) {
150 | if (_onMouseUp != null) {
151 | _onMouseUp({
152 | type: event.type,
153 | stageX: event.stageX,
154 | stageY: event.stageY
155 | });
156 | }
157 | }
158 |
159 | private var _onClick:TextInputEvent->Void = null;
160 | public var onClick(null, set):TextInputEvent->Void;
161 | private function set_onClick(value:TextInputEvent->Void):TextInputEvent->Void {
162 | if (_onClick != null) {
163 | tf.removeEventListener(openfl.events.MouseEvent.CLICK, __onTextInputClickEvent);
164 | }
165 | _onClick = value;
166 | if (_onClick != null) {
167 | tf.addEventListener(openfl.events.MouseEvent.CLICK, __onTextInputClickEvent);
168 | }
169 | return value;
170 | }
171 |
172 | private function __onTextInputClickEvent(event:openfl.events.MouseEvent) {
173 | if (_onClick != null) {
174 | _onClick({
175 | type: event.type,
176 | stageX: event.stageX,
177 | stageY: event.stageY
178 | });
179 | }
180 | }
181 |
182 | private var _onChange:TextInputEvent->Void = null;
183 | public var onChange(null, set):TextInputEvent->Void;
184 | private function set_onChange(value:TextInputEvent->Void):TextInputEvent->Void {
185 | if (_onChange != null) {
186 | tf.removeEventListener(Event.CHANGE, __onTextInputChangeEvent);
187 | }
188 | _onChange = value;
189 | if (_onChange != null) {
190 | tf.addEventListener(Event.CHANGE, __onTextInputChangeEvent);
191 | }
192 | return value;
193 | }
194 |
195 | private function __onTextInputChangeEvent(event:Event) {
196 | if (_onChange != null) {
197 | _onChange({
198 | type: event.type,
199 | stageX: 0,
200 | stageY: 0
201 | });
202 | }
203 | }
204 |
205 | private var _onKeyDown:KeyboardEvent->Void = null;
206 | public var onKeyDown(null, set):KeyboardEvent->Void;
207 | private function set_onKeyDown(value:KeyboardEvent->Void):KeyboardEvent->Void {
208 | if (_onKeyDown != null) {
209 | tf.removeEventListener(KeyboardEvent.KEY_DOWN, __onTextInputKeyDown);
210 | }
211 | _onKeyDown = value;
212 | if (_onKeyDown != null) {
213 | tf.addEventListener(KeyboardEvent.KEY_DOWN, __onTextInputKeyDown);
214 | }
215 | return value;
216 | }
217 |
218 | private function __onTextInputKeyDown(e:KeyboardEvent) {
219 | if (_onKeyDown != null)
220 | _onKeyDown(e);
221 | }
222 |
223 | private var _onKeyUp:KeyboardEvent->Void = null;
224 | public var onKeyUp(null, set):KeyboardEvent->Void;
225 | private function set_onKeyUp(value:KeyboardEvent->Void):KeyboardEvent->Void {
226 | if (_onKeyUp != null) {
227 | tf.removeEventListener(KeyboardEvent.KEY_UP, __onTextInputKeyUp);
228 | }
229 | _onKeyUp = value;
230 | if (_onKeyUp != null) {
231 | tf.addEventListener(KeyboardEvent.KEY_UP, __onTextInputKeyUp);
232 | }
233 | return value;
234 | }
235 |
236 | private function __onTextInputKeyUp(e:KeyboardEvent) {
237 | if (_onKeyUp != null)
238 | _onKeyUp(e);
239 | }
240 |
241 | public function addToComponent(component:Component) {
242 | FlxG.addChildBelowMouse(tf, 0xffffff);
243 | }
244 |
245 | public function equals(sprite:FlxSprite):Bool {
246 | return false;
247 | }
248 |
249 | private var _parentHidden:Bool = false;
250 | public function update() {
251 | var ref = parentComponent;
252 | // TODO: perf?
253 | while (ref != null) {
254 | if (ref.hidden) {
255 | tf.visible = false;
256 | _parentHidden = true;
257 | break;
258 | }
259 | ref = ref.parentComponent;
260 | }
261 |
262 | if (_parentHidden == true) {
263 | return;
264 | }
265 |
266 | tf.visible = true;
267 |
268 | var x1 = tf.x;
269 | var y1 = tf.y;
270 | var x2 = tf.x + tf.textWidth;
271 | var y2 = tf.y + tf.textHeight;
272 |
273 | var rc = new Rectangle(tf.x, tf.y, tf.textWidth, tf.textHeight);
274 |
275 | var components:Array = [];
276 | var overlaps:Bool = false;
277 | var after = false;
278 | for (r in Screen.instance.rootComponents) {
279 | if (parentComponent.rootComponent == r) {
280 | after = true;
281 | continue;
282 | }
283 |
284 | var rootRect = new Rectangle(r.screenLeft, r.screenTop, r.width, r.height);
285 | if (after == true && rootRect.intersects(rc)) {
286 | overlaps = true;
287 | break;
288 | }
289 | }
290 |
291 | /*
292 | if (overlaps == true && tf.visible == true) {
293 | tf.visible = false;
294 | } else if (overlaps == false && tf.visible == false) {
295 | tf.visible = true;
296 | }
297 | */
298 | }
299 |
300 | public function destroy(component:Component) {
301 | _parentHidden = true;
302 | tf.visible = false;
303 | FlxG.removeChild(tf);
304 | tf = null;
305 | }
306 |
307 | private override function validateData() {
308 | if (_text != null) {
309 | if (_dataSource == null) {
310 | tf.text = normalizeText(_text);
311 | }
312 | }
313 | }
314 |
315 | private function normalizeText(text:String):String {
316 | text = StringTools.replace(text, "\\n", "\n");
317 | return text;
318 | }
319 |
320 | private override function measureText() {
321 | tf.width = _width * Toolkit.scaleX;
322 |
323 | #if !flash
324 | _textWidth = tf.textWidth + PADDING_X;
325 | //_textWidth = textField.textWidth + PADDING_X;
326 | #else
327 | //_textWidth = textField.textWidth - 2;
328 | #end
329 | _textHeight = tf.textHeight;
330 | if (_textHeight == 0) {
331 | var tmpText:String = tf.text;
332 | tf.text = "|";
333 | _textHeight = tf.textHeight;
334 | tf.text = tmpText;
335 | }
336 | #if !flash
337 | //_textHeight += PADDING_Y;
338 | #else
339 | //_textHeight -= 2;
340 | #end
341 |
342 | _textWidth = Math.round(_textWidth) / Toolkit.scaleX;
343 | _textHeight = Math.round(_textHeight) / Toolkit.scaleY;
344 |
345 | //////////////////////////////////////////////////////////////////////////////
346 |
347 | _inputData.hscrollMax = tf.maxScrollH;
348 | // see below
349 | _inputData.hscrollPageSize = (_width * _inputData.hscrollMax) / _textWidth;
350 |
351 | _inputData.vscrollMax = tf.maxScrollV - 1;
352 | // cant have page size yet as there seems to be an openfl issue with bottomScrollV
353 | // https://github.com/openfl/openfl/issues/2220
354 | _inputData.vscrollPageSize = (_height * _inputData.vscrollMax) / _textHeight;
355 | }
356 |
357 | private override function validateStyle():Bool {
358 | var measureTextRequired:Bool = false;
359 |
360 | var format:TextFormat = tf.getTextFormat();
361 |
362 | if (_textStyle != null) {
363 | if (format.align != _textStyle.textAlign) {
364 | format.align = _textStyle.textAlign;
365 | }
366 |
367 | var fontSizeValue = Std.int(_textStyle.fontSize);
368 | if (_textStyle.fontSize == null) {
369 | //fontSizeValue = 13;
370 | }
371 | if (format.size != fontSizeValue) {
372 | format.size = Std.int(fontSizeValue * Toolkit.scale);
373 |
374 | measureTextRequired = true;
375 | }
376 |
377 | if (_fontInfo != null && format.font != _fontInfo.data) {
378 | format.font = _fontInfo.data;
379 | measureTextRequired = true;
380 | }
381 |
382 | if (format.color != _textStyle.color) {
383 | format.color = _textStyle.color;
384 | }
385 |
386 | if (format.bold != _textStyle.fontBold) {
387 | //format.bold = _textStyle.fontBold;
388 | measureTextRequired = true;
389 | }
390 |
391 | if (format.italic != _textStyle.fontItalic) {
392 | //format.italic = _textStyle.fontItalic;
393 | measureTextRequired = true;
394 | }
395 |
396 | if (format.underline != _textStyle.fontUnderline) {
397 | //format.underline = _textStyle.fontUnderline;
398 | measureTextRequired = true;
399 | }
400 | }
401 |
402 | tf.defaultTextFormat = format;
403 | tf.setTextFormat(format);
404 | if (tf.wordWrap != _displayData.wordWrap) {
405 | tf.wordWrap = _displayData.wordWrap;
406 | measureTextRequired = true;
407 | }
408 |
409 | if (tf.multiline != _displayData.multiline) {
410 | tf.multiline = _displayData.multiline;
411 | measureTextRequired = true;
412 | }
413 |
414 | if (tf.displayAsPassword != _inputData.password) {
415 | tf.displayAsPassword = _inputData.password;
416 | }
417 |
418 | tf.type = (parentComponent.disabled ? DYNAMIC : INPUT);
419 |
420 | return measureTextRequired;
421 | }
422 |
423 | private function onInternalChange(e:Event) {
424 | _text = tf.text;
425 |
426 | measureText();
427 |
428 | if (_inputData.onChangedCallback != null) {
429 | _inputData.onChangedCallback();
430 | }
431 | }
432 |
433 | private function onScroll(e) {
434 | _inputData.hscrollPos = tf.scrollH;
435 | _inputData.vscrollPos = tf.scrollV - 1;
436 |
437 | if (_inputData.onScrollCallback != null) {
438 | _inputData.onScrollCallback();
439 | }
440 | }
441 |
442 | private override function validatePosition() {
443 | _left = Math.round(_left * Toolkit.scaleX);
444 | _top = Math.round(_top * Toolkit.scaleY);
445 | }
446 |
447 | private override function validateDisplay() {
448 | if (tf.width != _width * Toolkit.scaleX) {
449 | tf.width = _width * Toolkit.scaleX;
450 | }
451 |
452 | if (tf.height != _height * Toolkit.scaleY) {
453 | #if flash
454 | tf.height = _height * Toolkit.scaleY;
455 | //textField.height = _height + 4;
456 | #else
457 | tf.height = _height * Toolkit.scaleY;
458 | #end
459 | }
460 | }
461 | }
--------------------------------------------------------------------------------
/haxe/ui/backend/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/haxelib.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.7.0",
3 | "contributors": [
4 | "haxeui",
5 | "MSGhero",
6 | "ianharrigan"
7 | ],
8 | "dependencies": {
9 | "haxeui-core": "",
10 | "flixel": ""
11 | },
12 | "license": "MIT",
13 | "tags": [
14 | "ui",
15 | "gui",
16 | "flixel"
17 | ],
18 | "releasenote": "1.7.0 release",
19 | "name": "haxeui-flixel",
20 | "description": "The Flixel backend of the HaxeUI framework",
21 | "url": "https://github.com/haxeui/haxeui-flixel"
22 | }
--------------------------------------------------------------------------------
/include.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------