├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md └── workflows │ ├── build.hxml │ └── build.yml ├── 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 │ ├── heaps │ ├── EventMapper.hx │ ├── EventType.hx │ ├── FilterConverter.hx │ ├── KeyboardHelper.hx │ ├── MouseHelper.hx │ ├── SDFFonts.hx │ ├── ScreenUtils.hx │ ├── StyleHelper.hx │ ├── TileCache.hx │ └── _module │ │ └── styles │ │ ├── dark │ │ └── main.css │ │ ├── default │ │ └── main.css │ │ └── main.css │ └── module.xml ├── haxelib.json └── haxeui-heaps.properties /.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.hxml: -------------------------------------------------------------------------------- 1 | -lib haxeui-core 2 | -lib heaps 3 | 4 | -hl main.hl 5 | 6 | -cp . 7 | --no-output 8 | --macro haxe.macro.Compiler.include("haxe.ui", ["haxe.ui.macros"]) 9 | -------------------------------------------------------------------------------- /.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.3.0, 4.3.1] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Setup Haxe (haxe ${{ matrix.haxe-version }}, ${{ matrix.os }}) 17 | uses: krdlab/setup-haxe@v1 18 | with: 19 | haxe-version: ${{ matrix.haxe-version }} 20 | 21 | - name: Setup app (haxe ${{ matrix.haxe-version }}, ${{ matrix.os }}) 22 | run: | 23 | git clone --branch master https://github.com/haxeui/haxeui-core.git --depth=1 24 | haxelib dev haxeui-core haxeui-core 25 | 26 | git clone --branch master https://github.com/HeapsIO/heaps.git 27 | haxelib dev heaps heaps 28 | 29 | haxelib install hlsdl 30 | 31 | mkdir res 32 | 33 | - name: Build app (haxe ${{ matrix.haxe-version }}, ${{ matrix.os }}) 34 | run: | 35 | cp .github/workflows/build.hxml build.hxml 36 | haxelib install build.hxml --always --quiet 37 | haxe build.hxml 38 | -------------------------------------------------------------------------------- /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 | ![build status](https://github.com/haxeui/haxeui-heaps/actions/workflows/build.yml/badge.svg) 2 | 3 | # haxeui-heaps 4 | `haxeui-heaps` is the `Heaps` backend for `HaxeUI`. 5 | 6 | ## Installation 7 | `haxeui-heaps` relies on `haxeui-core` as well as `Heaps`. To install: 8 | 9 | ``` 10 | haxelib install heaps 11 | haxelib install haxeui-core 12 | haxelib install haxeui-heaps 13 | ``` 14 | 15 | ### Toolkit initialization and usage 16 | Before you start using `HaxeUI` in your project, you must first initialize the `Toolkit`. 17 | 18 | ```haxe 19 | Toolkit.init(); 20 | ``` 21 | 22 | Once the toolkit is initialized, you can add components using the methods specified here. 23 | 24 | ```haxe 25 | var app = new HaxeUIApp(); 26 | app.ready( 27 | function() { 28 | var main = ComponentMacros.buildComponent("assets/xml/test.xml"); // whatever your XML layout path is 29 | app.addComponent(main); 30 | app.start(); 31 | } 32 | ); 33 | ``` 34 | 35 | Some examples are [here](https://github.com/haxeui/component-examples). 36 | 37 | ## Addtional resources 38 | * component-explorer - Browse HaxeUI components 39 | * playground - Write and test HaxeUI layouts in your browser 40 | * component-examples - Various componet examples 41 | * haxeui-api - The HaxeUI api docs. 42 | * haxeui-guides - Set of guides to working with HaxeUI and backends. 43 | -------------------------------------------------------------------------------- /classpath.exclusions: -------------------------------------------------------------------------------- 1 | ; exclude paths from classpath when searching for haxeui arifacts (module.xml, native.xml, etc) 2 | ; speeds up build 3 | \/heaps\/ 4 | \/hlopenal\/ 5 | \/hlsdl\/ 6 | 7 | \/format\/ 8 | -------------------------------------------------------------------------------- /extraParams.hxml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxeui/haxeui-heaps/396f1a0704bcd8836637b3cc87a2691caafd0232/extraParams.hxml -------------------------------------------------------------------------------- /haxe/ui/backend/AppImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | import haxe.ui.core.Screen; 3 | 4 | private class HeapsApp extends hxd.App { 5 | public var onInit:Void->Void = null; 6 | public var onUpdate:Float->Void = null; 7 | 8 | private override function init() { 9 | super.init(); 10 | Screen.instance.root = this.s2d; 11 | if (onInit != null) { 12 | onInit(); 13 | } 14 | } 15 | 16 | private override function update(dt:Float) { 17 | if (onUpdate != null) { 18 | onUpdate(dt); 19 | } 20 | } 21 | } 22 | 23 | class AppImpl extends AppBase { 24 | private var _app:HeapsApp; 25 | 26 | public function new() { 27 | _autoHandlePreload = false; 28 | _app = new HeapsApp(); 29 | _app.onInit = onHeapsInit; 30 | _app.onUpdate = onHeapsUpdate; 31 | } 32 | 33 | private var _heapsInitialized:Bool = false; 34 | private var _heapsReadyCalled:Bool = false; 35 | private function onHeapsInit() { 36 | #if js 37 | //hxd.Res.initLocal(); 38 | hxd.Res.initEmbed(); 39 | #else 40 | hxd.Res.initEmbed(); 41 | #end 42 | 43 | if (Toolkit.backendProperties.exists("haxe.ui.heaps.engine.background.color")) { 44 | h3d.Engine.getCurrent().backgroundColor = Toolkit.backendProperties.getPropCol("haxe.ui.heaps.engine.background.color"); 45 | } 46 | _heapsInitialized = true; 47 | if (__onReady != null) { 48 | startPreload(function() { 49 | _heapsReadyCalled = true; 50 | __onReady(); 51 | }); 52 | } 53 | } 54 | 55 | private function onHeapsUpdate(dt:Float) { 56 | BackendImpl.update(); 57 | } 58 | 59 | private var __onReady:Void->Void; 60 | private override function init(onReady:Void->Void, onEnd:Void->Void = null) { 61 | __onReady = onReady; 62 | if (_heapsInitialized == true && _heapsReadyCalled == false) { 63 | __onReady(); 64 | } 65 | } 66 | 67 | private override function getToolkitInit():ToolkitOptions { 68 | return { 69 | root: _app.s2d, 70 | manualUpdate: true 71 | }; 72 | } 73 | } -------------------------------------------------------------------------------- /haxe/ui/backend/AssetsImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import haxe.io.Bytes; 4 | import haxe.ui.assets.FontInfo; 5 | import hxd.Res; 6 | import hxd.fs.BytesFileSystem.BytesFileEntry; 7 | import hxd.res.Image; 8 | 9 | class AssetsImpl extends AssetsBase { 10 | public function embedFontSupported():Bool { 11 | return #if (lime || flash || js) true #else false #end; 12 | } 13 | 14 | private override function getImageInternal(resourceId:String, callback:haxe.ui.assets.ImageInfo->Void) { 15 | try { 16 | var loader:hxd.res.Loader = hxd.Res.loader; 17 | if (loader != null) { 18 | if (loader.exists(resourceId)) { 19 | var image:Image = loader.load(resourceId).toImage(); 20 | var size:Dynamic = image.getSize(); 21 | var imageInfo:haxe.ui.assets.ImageInfo = { 22 | width: size.width, 23 | height: size.height, 24 | data: image.toBitmap() 25 | }; 26 | callback(imageInfo); 27 | } else { 28 | callback(null); 29 | } 30 | } else { 31 | callback(null); 32 | } 33 | } catch (e:Dynamic) { 34 | trace(e); 35 | callback(null); 36 | } 37 | } 38 | 39 | private override function getImageFromHaxeResource(resourceId:String, callback:String->haxe.ui.assets.ImageInfo->Void) { 40 | var bytes = Resource.getBytes(resourceId); 41 | imageFromBytes(bytes, function(imageInfo) { 42 | callback(resourceId, imageInfo); 43 | }); 44 | } 45 | 46 | public override function imageFromBytes(bytes:Bytes, callback:haxe.ui.assets.ImageInfo->Void) { 47 | if (bytes == null) { 48 | callback(null); 49 | return; 50 | } 51 | 52 | try { 53 | var entry:BytesFileEntry = new BytesFileEntry("", bytes); 54 | var image:Image = new Image(entry); 55 | 56 | var size:Dynamic = image.getSize(); 57 | var imageInfo:haxe.ui.assets.ImageInfo = { 58 | width: size.width, 59 | height: size.height, 60 | data: image.toBitmap() 61 | }; 62 | callback(imageInfo); 63 | } catch (e:Dynamic) { 64 | callback(null); 65 | } 66 | } 67 | 68 | public override function imageInfoFromImageData(imageData:ImageData):haxe.ui.assets.ImageInfo { 69 | var imageInfo:haxe.ui.assets.ImageInfo = { 70 | width: imageData.width, 71 | height: imageData.height, 72 | data: imageData 73 | }; 74 | return imageInfo; 75 | } 76 | 77 | private override function getFontInternal(resourceId:String, callback:FontInfo->Void) { 78 | try { 79 | var font = hxd.Res.loader.loadCache(resourceId, hxd.res.BitmapFont); 80 | callback({ 81 | name: resourceId, 82 | data: font 83 | }); 84 | } catch (error:Dynamic) { 85 | #if debug 86 | trace("WARNING: problem loading font '" + resourceId + "' (" + error + ")"); 87 | #end 88 | callback(null); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /haxe/ui/backend/BackendImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | class BackendImpl { 4 | public static var id:String = "heaps"; 5 | 6 | public static function update() { 7 | TimerImpl.update(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /haxe/ui/backend/CallLaterImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | class CallLaterImpl { 4 | public function new(fn:Void->Void) { 5 | haxe.ui.util.Timer.delay(fn, 0); 6 | //MainLoop.runInMainThread(fn); 7 | } 8 | } -------------------------------------------------------------------------------- /haxe/ui/backend/ComponentGraphicsImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import h2d.Bitmap; 4 | import h2d.Graphics; 5 | import h2d.Tile; 6 | import h3d.mat.Texture; 7 | import haxe.io.Bytes; 8 | import haxe.ui.core.Component; 9 | import haxe.ui.util.Color; 10 | import haxe.ui.util.Variant; 11 | import hxd.PixelFormat; 12 | import hxd.Pixels; 13 | 14 | class ComponentGraphicsImpl extends ComponentGraphicsBase { 15 | private var _styleGraphics:Graphics = null; 16 | private var _hasSize:Bool = false; 17 | 18 | public function new(component:Component) { 19 | super(component); 20 | component.styleable = false; 21 | createGraphics(); 22 | } 23 | 24 | public override function clear() { 25 | if (_hasSize == false) { 26 | return super.clear(); 27 | } 28 | _styleGraphics.clear(); 29 | } 30 | 31 | public override function setPixel(x:Float, y:Float, color:Color) { 32 | if (_hasSize == false) { 33 | return super.setPixel(x, y, color); 34 | } 35 | } 36 | 37 | private var _bitmap:Bitmap = null; 38 | private var _texture:Texture = null; 39 | public override function setPixels(pixels:Bytes) { 40 | if (_hasSize == false) { 41 | return super.setPixels(pixels); 42 | } 43 | 44 | if (_bitmap == null) { 45 | _bitmap = new Bitmap(); 46 | _component.addChild(_bitmap); 47 | } 48 | 49 | var p = new Pixels(Std.int(_component.width), Std.int(_component.height), pixels, PixelFormat.RGBA); 50 | if (_texture == null) { 51 | _texture = Texture.fromPixels(p); 52 | _bitmap.tile = Tile.fromTexture(_texture); 53 | } else { 54 | if (_texture.width != _component.width || _texture.height != _component.height) { 55 | _texture.resize(Std.int(_component.width), Std.int(_component.height)); 56 | _bitmap.tile = Tile.fromTexture(_texture); // To ensure size is correct. 57 | } 58 | _texture.uploadPixels(p); 59 | } 60 | } 61 | 62 | public override function moveTo(x:Float, y:Float) { 63 | if (_hasSize == false) { 64 | return super.moveTo(x, y); 65 | } 66 | _styleGraphics.moveTo(x, y); 67 | } 68 | 69 | public override function lineTo(x:Float, y:Float) { 70 | if (_hasSize == false) { 71 | return super.lineTo(x, y); 72 | } 73 | _styleGraphics.lineTo(x, y); 74 | } 75 | 76 | public override function strokeStyle( color:Null, thickness:Null = 1, alpha:Null = 1) { 77 | if (_hasSize == false) { 78 | return super.strokeStyle(color, thickness, alpha); 79 | } 80 | _styleGraphics.lineStyle(thickness, color, alpha); 81 | } 82 | 83 | public override function circle(x:Float, y:Float, radius:Float) { 84 | if (_hasSize == false) { 85 | return super.circle(x, y, radius); 86 | } 87 | _styleGraphics.drawCircle(x, y, radius, Std.int(radius * 10)); 88 | } 89 | 90 | public override function fillStyle(color:Null, alpha:Null = 1) { 91 | if (_hasSize == false) { 92 | return super.fillStyle(color, alpha); 93 | } 94 | if (color == null) { 95 | _styleGraphics.endFill(); 96 | return; 97 | } 98 | _styleGraphics.beginFill(color, alpha); 99 | } 100 | 101 | public override function curveTo(controlX:Float, controlY:Float, anchorX:Float, anchorY:Float) { 102 | if (_hasSize == false) { 103 | return super.curveTo(controlX, controlY, anchorX, anchorY); 104 | } 105 | _styleGraphics.curveTo(controlX, controlY, anchorX, anchorY); 106 | } 107 | 108 | public override function cubicCurveTo(controlX1:Float, controlY1:Float, controlX2:Float, controlY2:Float, anchorX:Float, anchorY:Float) { 109 | if (_hasSize == false) { 110 | return super.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY); 111 | } 112 | _styleGraphics.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY); 113 | } 114 | 115 | public override function rectangle(x:Float, y:Float, width:Float, height:Float) { 116 | if (_hasSize == false) { 117 | return super.rectangle(x, y, width, height); 118 | } 119 | _styleGraphics.drawRect(x, y, width, height); 120 | } 121 | 122 | public override function image(resource:Variant, x:Null = null, y:Null = null, width:Null = null, height:Null = null) { 123 | if (_hasSize == false) { 124 | return super.image(resource, x, y, width, height); 125 | } 126 | } 127 | 128 | private function createGraphics() { 129 | var container = _component.getChildAt(0); // first child is always the style-objects container 130 | if (container == null) { 131 | return; // fix crash resizing the window; container doesn't exist yet 132 | } 133 | 134 | _styleGraphics = cast(container.getObjectByName("styleGraphics"), Graphics); 135 | if (_styleGraphics == null) { 136 | _styleGraphics = new Graphics(); 137 | _styleGraphics.name = "styleGraphics"; 138 | container.addChildAt(_styleGraphics, 0); 139 | } 140 | } 141 | 142 | public override function resize(width:Null, height:Null) { 143 | if (width > 0 && height > 0) { 144 | if (_hasSize == false) { 145 | _hasSize = true; 146 | replayDrawCommands(); 147 | } 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /haxe/ui/backend/ComponentImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import h2d.Camera; 4 | import h2d.Graphics; 5 | import h2d.Object; 6 | import h2d.RenderContext; 7 | import h2d.Scene; 8 | import h2d.col.Point; 9 | import h2d.filter.Filter; 10 | import h2d.filter.Group; 11 | import h2d.filter.Mask; 12 | import haxe.ui.Toolkit; 13 | import haxe.ui.backend.heaps.FilterConverter; 14 | import haxe.ui.backend.heaps.MouseHelper; 15 | import haxe.ui.backend.heaps.StyleHelper; 16 | import haxe.ui.core.Component; 17 | import haxe.ui.core.ImageDisplay; 18 | import haxe.ui.core.Screen; 19 | import haxe.ui.core.TextDisplay; 20 | import haxe.ui.core.TextInput; 21 | import haxe.ui.events.KeyboardEvent; 22 | import haxe.ui.events.MouseEvent; 23 | import haxe.ui.events.UIEvent; 24 | import haxe.ui.geom.Rectangle; 25 | import haxe.ui.styles.Style; 26 | import haxe.ui.util.MathUtil; 27 | import haxe.ui.validation.InvalidationFlags; 28 | 29 | class ComponentImpl extends ComponentBase { 30 | public var styleable:Bool = true; 31 | 32 | @:noCompletion 33 | private var _eventMap:MapVoid>; 34 | @:noCompletion 35 | private var _container:Object = null; 36 | 37 | @:noCompletion 38 | static inline var INDEX_OFFSET = 1; // offset everything because 0th-child is always the style graphics container 39 | 40 | public function new() { 41 | super(); 42 | _eventMap = new MapVoid>(); 43 | _container = new Object(); 44 | _container.name = "container"; 45 | var styleGraphics = new Graphics(); 46 | styleGraphics.name = "styleGraphics"; 47 | _container.addChild(styleGraphics); 48 | addChild(_container); // style graphics container 49 | //cast(this, Component).ready(); 50 | } 51 | 52 | @:noCompletion 53 | private override function handlePosition(left:Null, top:Null, style:Style) { 54 | if (left == null || top == null) { 55 | return; 56 | } 57 | 58 | left = Math.fround(left); 59 | top = Math.fround(top); 60 | 61 | if (this.x != left) this.x = left; 62 | if (this.y != top) this.y = top; 63 | } 64 | 65 | @:noCompletion 66 | private override function handleSize(w:Null, h:Null, style:Style) { 67 | if (h == null || w == null || w <= 0 || h <= 0) { 68 | return; 69 | } 70 | 71 | if (this.styleable) { 72 | StyleHelper.apply(this, style, w, h); 73 | } 74 | } 75 | 76 | @:noCompletion 77 | private override function handleVisibility(show:Bool) { 78 | super.visible = show; 79 | } 80 | 81 | @:noCompletion 82 | private var _maskGraphics:Graphics = null; 83 | @:noCompletion 84 | private override function handleClipRect(value:Rectangle) { 85 | if (_maskGraphics == null) { 86 | _maskGraphics = new Graphics(); 87 | _maskGraphics.name = "maskGraphics"; 88 | 89 | _container.addChildAt(_maskGraphics, 0); 90 | _maskFilter = new Mask(_maskGraphics); 91 | this.filter = createFilterGroup(); 92 | } 93 | 94 | var borderSize:Float = 0; 95 | if (parentComponent != null && parentComponent.style == null) { 96 | parentComponent.validateNow(); 97 | } 98 | borderSize = parentComponent.style.borderSize; 99 | 100 | _maskGraphics.clear(); 101 | _maskGraphics.beginFill(0xFF00FF, 1.0); 102 | _maskGraphics.drawRect(0, 0, value.width, value.height); 103 | _maskGraphics.endFill(); 104 | _maskGraphics.x = value.left;//this.left; 105 | _maskGraphics.y = value.top;//this.top; 106 | 107 | // is this a hack? We dont want to move the component if the clip rect is the 108 | // full size of the component (like in the case of clip:true), feels wrong 109 | // for some reason, but without this, clip:true components move around 110 | // when they shouldnt (which i dont fully understand) 111 | if (this.width != value.width) { 112 | // multiple masks / clip rects in the same component (like tableview) can interfere with each 113 | // other in heaps, so lets find them and update our co-ords appropriately 114 | var offsetX:Float = 0; 115 | for (c in this.parentComponent.childComponents) { 116 | if (c._maskGraphics != null && c._maskGraphics != this._maskGraphics) { 117 | var clipComponent = c.findClipComponent(); 118 | if (clipComponent != null && clipComponent.width != this.width) { 119 | trace(clipComponent.width, this.width); 120 | offsetX += clipComponent.width; 121 | } 122 | } 123 | } 124 | this.x = -value.left + borderSize + offsetX; 125 | } 126 | if (this.height != value.height) { 127 | // multiple masks / clip rects in the same component (like tableview) can interfere with each 128 | // other in heaps, so lets find them and update our co-ords appropriately 129 | var offsetY:Float = 0; 130 | for (c in this.parentComponent.childComponents) { 131 | if (c._maskGraphics != null && c._maskGraphics != this._maskGraphics) { 132 | var clipComponent = c.findClipComponent(); 133 | if (clipComponent != null && clipComponent.height != this.height) { 134 | offsetY += clipComponent.height; 135 | } 136 | } 137 | } 138 | this.y = -value.top + borderSize + offsetY; 139 | } 140 | } 141 | 142 | //*********************************************************************************************************** 143 | // Text related 144 | //*********************************************************************************************************** 145 | @:noCompletion 146 | public override function createTextDisplay(text:String = null):TextDisplay { 147 | if (_textDisplay == null) { 148 | super.createTextDisplay(text); 149 | addChild(_textDisplay.sprite); 150 | } 151 | 152 | return _textDisplay; 153 | } 154 | 155 | @:noCompletion 156 | public override function createTextInput(text:String = null):TextInput { 157 | if (_textInput == null) { 158 | super.createTextInput(text); 159 | addChild(_textInput.sprite); 160 | } 161 | 162 | return _textInput; 163 | } 164 | 165 | //*********************************************************************************************************** 166 | // Image related 167 | //*********************************************************************************************************** 168 | @:noCompletion 169 | public override function createImageDisplay():ImageDisplay { 170 | if (_imageDisplay == null) { 171 | super.createImageDisplay(); 172 | addChild(_imageDisplay.sprite); 173 | } 174 | 175 | return _imageDisplay; 176 | } 177 | 178 | @:noCompletion 179 | public override function removeImageDisplay() { 180 | if (_imageDisplay != null) { 181 | removeChild(_imageDisplay.sprite); 182 | _imageDisplay.dispose(); 183 | _imageDisplay = null; 184 | } 185 | } 186 | 187 | //*********************************************************************************************************** 188 | // Display tree 189 | //*********************************************************************************************************** 190 | @:noCompletion 191 | private override function handleReady() { 192 | super.handleReady(); 193 | @:privateAccess Screen.instance.addUpdateCallback(); 194 | } 195 | 196 | @:noCompletion 197 | private override function handleSetComponentIndex(child:Component, index:Int) { 198 | addChildAt(child, index + INDEX_OFFSET); 199 | } 200 | 201 | @:noCompletion 202 | private override function handleAddComponent(child:Component):Component { 203 | addChild(child); 204 | return child; 205 | } 206 | 207 | @:noCompletion 208 | private override function handleAddComponentAt(child:Component, index:Int):Component { 209 | addChildAt(child, index + INDEX_OFFSET); 210 | return child; 211 | } 212 | 213 | @:noCompletion 214 | private override function handleRemoveComponent(child:Component, dispose:Bool = true):Component { 215 | removeChild(child); 216 | 217 | if (dispose == true) { 218 | child.dispose(); 219 | } 220 | 221 | return child; 222 | } 223 | 224 | @:noCompletion 225 | private override function handleRemoveComponentAt(index:Int, dispose:Bool = true):Component { 226 | var child = _children[index]; 227 | if (child != null) { 228 | removeChild(child); 229 | 230 | if (dispose == true) { 231 | child.dispose(); 232 | } 233 | } 234 | return child; 235 | } 236 | 237 | @:noCompletion 238 | private var _deallocate:Bool = false; 239 | @:noCompletion 240 | private var deallocate(null, set):Bool; 241 | @:noCompletion 242 | private function set_deallocate(value:Bool) { 243 | _deallocate = value; 244 | for (c in this.childComponents) { 245 | c.deallocate = value; 246 | } 247 | return value; 248 | } 249 | 250 | @:noCompletion 251 | private var _disposed:Bool = false; 252 | @:noCompletion 253 | private function dispose() { 254 | if (_disposed == true) { 255 | return; 256 | } 257 | deallocate = true; 258 | removeChildren(); 259 | _maskGraphics = null; 260 | remove(); 261 | } 262 | 263 | @:noCompletion 264 | private var _currentStyleFilters:Array = null; 265 | @:noCompletion 266 | private var _maskFilter:Mask = null; 267 | @:noCompletion 268 | private function createFilterGroup() { 269 | var n = 0; 270 | var filterGroup = new Group(); 271 | if (_maskFilter != null) { 272 | filterGroup.add(_maskFilter); 273 | n++; 274 | } 275 | if (_currentStyleFilters != null) { 276 | for (f in _currentStyleFilters) { 277 | filterGroup.add(f); 278 | n++; 279 | } 280 | } 281 | if (n == 0) { 282 | return null; 283 | } 284 | return filterGroup; 285 | } 286 | 287 | @:noCompletion 288 | private override function applyStyle(style:Style) { 289 | /* 290 | if (style.cursor != null && style.cursor == "pointer") { 291 | cursor = Cursor.Button; 292 | } else if (cursor != hxd.Cursor.Default) { 293 | cursor = Cursor.Default; 294 | } 295 | */ 296 | if (style.filter != null && style.filter.length > 0) { 297 | _currentStyleFilters = []; 298 | for (f in style.filter) { 299 | var filter = FilterConverter.convertFilter(f); 300 | if (filter != null) { 301 | _currentStyleFilters.push(filter); 302 | } 303 | } 304 | this.filter = createFilterGroup(); 305 | } else { 306 | _currentStyleFilters = null; 307 | this.filter = createFilterGroup(); 308 | } 309 | 310 | if (style.hidden != null) { 311 | visible = !style.hidden; 312 | } 313 | 314 | if (style.opacity != null) { 315 | alpha = style.opacity; 316 | } 317 | } 318 | 319 | //*********************************************************************************************************** 320 | // Events 321 | //*********************************************************************************************************** 322 | @:noCompletion 323 | @:access(haxe.ui.core.Screen) 324 | private override function mapEvent(type:String, listener:UIEvent->Void) { 325 | switch (type) { 326 | case MouseEvent.MOUSE_MOVE: 327 | if (_eventMap.exists(MouseEvent.MOUSE_MOVE) == false) { 328 | MouseHelper.notify(MouseEvent.MOUSE_MOVE, __onMouseMove); 329 | _eventMap.set(MouseEvent.MOUSE_MOVE, listener); 330 | } 331 | 332 | case MouseEvent.MOUSE_OVER: 333 | if (_eventMap.exists(MouseEvent.MOUSE_OVER) == false) { 334 | MouseHelper.notify(MouseEvent.MOUSE_MOVE, __onMouseMove); 335 | _eventMap.set(MouseEvent.MOUSE_OVER, listener); 336 | } 337 | 338 | case MouseEvent.MOUSE_OUT: 339 | if (_eventMap.exists(MouseEvent.MOUSE_OUT) == false) { 340 | _eventMap.set(MouseEvent.MOUSE_OUT, listener); 341 | } 342 | 343 | case MouseEvent.MOUSE_DOWN: 344 | if (_eventMap.exists(MouseEvent.MOUSE_DOWN) == false) { 345 | MouseHelper.notify(MouseEvent.MOUSE_DOWN, __onMouseDown); 346 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 347 | _eventMap.set(MouseEvent.MOUSE_DOWN, listener); 348 | } 349 | 350 | case MouseEvent.MOUSE_UP: 351 | if (_eventMap.exists(MouseEvent.MOUSE_UP) == false) { 352 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 353 | _eventMap.set(MouseEvent.MOUSE_UP, listener); 354 | } 355 | 356 | case MouseEvent.MOUSE_WHEEL: 357 | if (_eventMap.exists(MouseEvent.MOUSE_WHEEL) == false) { 358 | MouseHelper.notify(MouseEvent.MOUSE_MOVE, __onMouseMove); 359 | MouseHelper.notify(MouseEvent.MOUSE_WHEEL, __onMouseWheel); 360 | _eventMap.set(MouseEvent.MOUSE_WHEEL, listener); 361 | } 362 | 363 | case MouseEvent.CLICK: 364 | if (_eventMap.exists(MouseEvent.CLICK) == false) { 365 | _eventMap.set(MouseEvent.CLICK, listener); 366 | 367 | if (_eventMap.exists(MouseEvent.MOUSE_DOWN) == false) { 368 | MouseHelper.notify(MouseEvent.MOUSE_DOWN, __onMouseDown); 369 | _eventMap.set(MouseEvent.MOUSE_DOWN, null); 370 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 371 | _eventMap.set(MouseEvent.MOUSE_UP, null); 372 | } 373 | 374 | if (_eventMap.exists(MouseEvent.MOUSE_UP) == false) { 375 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 376 | _eventMap.set(MouseEvent.MOUSE_UP, null); 377 | } 378 | } 379 | 380 | case MouseEvent.DBL_CLICK: 381 | if (_eventMap.exists(MouseEvent.DBL_CLICK) == false) { 382 | _eventMap.set(MouseEvent.DBL_CLICK, listener); 383 | 384 | if (_eventMap.exists(MouseEvent.MOUSE_UP) == false) { 385 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onDoubleClick); 386 | _eventMap.set(MouseEvent.MOUSE_UP, listener); 387 | } 388 | } 389 | 390 | case MouseEvent.RIGHT_MOUSE_DOWN: 391 | if (_eventMap.exists(MouseEvent.RIGHT_MOUSE_DOWN) == false) { 392 | MouseHelper.notify(MouseEvent.MOUSE_DOWN, __onMouseDown); 393 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 394 | _eventMap.set(MouseEvent.RIGHT_MOUSE_DOWN, listener); 395 | } 396 | 397 | case MouseEvent.RIGHT_MOUSE_UP: 398 | if (_eventMap.exists(MouseEvent.RIGHT_MOUSE_UP) == false) { 399 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 400 | _eventMap.set(MouseEvent.RIGHT_MOUSE_UP, listener); 401 | } 402 | 403 | case MouseEvent.RIGHT_CLICK: 404 | if (_eventMap.exists(MouseEvent.RIGHT_CLICK) == false) { 405 | _eventMap.set(MouseEvent.RIGHT_CLICK, listener); 406 | 407 | if (_eventMap.exists(MouseEvent.RIGHT_MOUSE_DOWN) == false) { 408 | MouseHelper.notify(MouseEvent.MOUSE_DOWN, __onMouseDown); 409 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 410 | _eventMap.set(MouseEvent.RIGHT_MOUSE_DOWN, listener); 411 | } 412 | 413 | if (_eventMap.exists(MouseEvent.RIGHT_MOUSE_UP) == false) { 414 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 415 | _eventMap.set(MouseEvent.RIGHT_MOUSE_UP, listener); 416 | } 417 | } 418 | 419 | case MouseEvent.MIDDLE_MOUSE_DOWN: 420 | if (_eventMap.exists(MouseEvent.MIDDLE_MOUSE_DOWN) == false) { 421 | MouseHelper.notify(MouseEvent.MOUSE_DOWN, __onMouseDown); 422 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 423 | _eventMap.set(MouseEvent.MIDDLE_MOUSE_DOWN, listener); 424 | } 425 | 426 | case MouseEvent.MIDDLE_MOUSE_UP: 427 | if (_eventMap.exists(MouseEvent.MIDDLE_MOUSE_UP) == false) { 428 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 429 | _eventMap.set(MouseEvent.MIDDLE_MOUSE_UP, listener); 430 | } 431 | 432 | case MouseEvent.MIDDLE_CLICK: 433 | if (_eventMap.exists(MouseEvent.MIDDLE_CLICK) == false) { 434 | _eventMap.set(MouseEvent.MIDDLE_CLICK, listener); 435 | 436 | if (_eventMap.exists(MouseEvent.MIDDLE_MOUSE_DOWN) == false) { 437 | MouseHelper.notify(MouseEvent.MOUSE_DOWN, __onMouseDown); 438 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 439 | _eventMap.set(MouseEvent.MIDDLE_MOUSE_DOWN, listener); 440 | } 441 | 442 | if (_eventMap.exists(MouseEvent.MIDDLE_MOUSE_UP) == false) { 443 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 444 | _eventMap.set(MouseEvent.MIDDLE_MOUSE_UP, listener); 445 | } 446 | } 447 | 448 | case KeyboardEvent.KEY_DOWN: 449 | if (hasTextInput() && !_eventMap.exists(KeyboardEvent.KEY_DOWN)) { 450 | _eventMap.set(KeyboardEvent.KEY_DOWN, listener); 451 | getTextInput().onKeyDown = listener; 452 | } 453 | 454 | case KeyboardEvent.KEY_UP: 455 | if (hasTextInput() && !_eventMap.exists(KeyboardEvent.KEY_UP)) { 456 | _eventMap.set(KeyboardEvent.KEY_UP, listener); 457 | getTextInput().onKeyUp = listener; 458 | } 459 | 460 | case KeyboardEvent.KEY_PRESS: 461 | if (hasTextInput() && !_eventMap.exists(KeyboardEvent.KEY_PRESS)) { 462 | _eventMap.set(KeyboardEvent.KEY_PRESS, listener); 463 | getTextInput().onKeyPress = listener; 464 | } 465 | } 466 | } 467 | 468 | @:noCompletion 469 | private override function unmapEvent(type:String, listener:UIEvent->Void) { 470 | switch (type) { 471 | case MouseEvent.MOUSE_MOVE: 472 | _eventMap.remove(type); 473 | if (_eventMap.exists(MouseEvent.MOUSE_MOVE) == false 474 | && _eventMap.exists(MouseEvent.MOUSE_OVER) == false 475 | && _eventMap.exists(MouseEvent.MOUSE_WHEEL) == false) { 476 | MouseHelper.remove(MouseEvent.MOUSE_MOVE, __onMouseMove); 477 | } 478 | 479 | case MouseEvent.MOUSE_OVER: 480 | _eventMap.remove(type); 481 | if (_eventMap.exists(MouseEvent.MOUSE_MOVE) == false 482 | && _eventMap.exists(MouseEvent.MOUSE_OVER) == false 483 | && _eventMap.exists(MouseEvent.MOUSE_WHEEL) == false) { 484 | MouseHelper.remove(MouseEvent.MOUSE_MOVE, __onMouseMove); 485 | } 486 | 487 | case MouseEvent.MOUSE_OUT: 488 | _eventMap.remove(type); 489 | 490 | case MouseEvent.MOUSE_DOWN: 491 | _eventMap.remove(type); 492 | if (_eventMap.exists(MouseEvent.MOUSE_DOWN) == false 493 | && _eventMap.exists(MouseEvent.RIGHT_MOUSE_DOWN) == false) { 494 | MouseHelper.remove(MouseEvent.MOUSE_DOWN, __onMouseDown); 495 | } 496 | 497 | case MouseEvent.MOUSE_UP: 498 | _eventMap.remove(type); 499 | if (_eventMap.exists(MouseEvent.MOUSE_UP) == false 500 | && _eventMap.exists(MouseEvent.RIGHT_MOUSE_UP) == false) { 501 | MouseHelper.remove(MouseEvent.MOUSE_UP, __onMouseUp); 502 | } 503 | 504 | case MouseEvent.MOUSE_WHEEL: 505 | _eventMap.remove(type); 506 | MouseHelper.remove(MouseEvent.MOUSE_WHEEL, __onMouseWheel); 507 | if (_eventMap.exists(MouseEvent.MOUSE_MOVE) == false 508 | && _eventMap.exists(MouseEvent.MOUSE_OVER) == false 509 | && _eventMap.exists(MouseEvent.MOUSE_WHEEL) == false) { 510 | MouseHelper.remove(MouseEvent.MOUSE_MOVE, __onMouseMove); 511 | } 512 | 513 | case MouseEvent.CLICK: 514 | _eventMap.remove(type); 515 | 516 | case MouseEvent.DBL_CLICK: 517 | _eventMap.remove(type); 518 | MouseHelper.remove(MouseEvent.MOUSE_UP, __onDoubleClick); 519 | 520 | case MouseEvent.RIGHT_MOUSE_DOWN: 521 | _eventMap.remove(type); 522 | if (_eventMap.exists(MouseEvent.MOUSE_DOWN) == false 523 | && _eventMap.exists(MouseEvent.RIGHT_MOUSE_DOWN) == false) { 524 | MouseHelper.remove(MouseEvent.MOUSE_DOWN, __onMouseDown); 525 | } 526 | 527 | case MouseEvent.RIGHT_MOUSE_UP: 528 | _eventMap.remove(type); 529 | if (_eventMap.exists(MouseEvent.MOUSE_UP) == false 530 | && _eventMap.exists(MouseEvent.RIGHT_MOUSE_UP) == false) { 531 | MouseHelper.remove(MouseEvent.MOUSE_UP, __onMouseUp); 532 | } 533 | 534 | case MouseEvent.RIGHT_CLICK: 535 | _eventMap.remove(type); 536 | 537 | case MouseEvent.MIDDLE_MOUSE_DOWN: 538 | _eventMap.remove(type); 539 | if (_eventMap.exists(MouseEvent.MOUSE_DOWN) == false 540 | && _eventMap.exists(MouseEvent.MIDDLE_MOUSE_DOWN) == false) { 541 | MouseHelper.remove(MouseEvent.MOUSE_DOWN, __onMouseDown); 542 | } 543 | 544 | case MouseEvent.MIDDLE_MOUSE_UP: 545 | _eventMap.remove(type); 546 | if (_eventMap.exists(MouseEvent.MOUSE_UP) == false 547 | && _eventMap.exists(MouseEvent.MIDDLE_MOUSE_UP) == false) { 548 | MouseHelper.remove(MouseEvent.MOUSE_UP, __onMouseUp); 549 | } 550 | 551 | case MouseEvent.MIDDLE_CLICK: 552 | _eventMap.remove(type); 553 | } 554 | } 555 | 556 | // lets cache certain items so we dont have to loop multiple times per frame 557 | @:noCompletion 558 | private var _cachedScreenX:Null = null; 559 | @:noCompletion 560 | private var _cachedScreenY:Null = null; 561 | @:noCompletion 562 | private var _cachedClipComponent:Component = null; 563 | @:noCompletion 564 | private var _cachedClipComponentNone:Null = null; 565 | @:noCompletion 566 | private var _cachedRootComponent:Component = null; 567 | 568 | @:noCompletion 569 | private function clearCaches() { 570 | _cachedScreenX = null; 571 | _cachedScreenY = null; 572 | _cachedClipComponent = null; 573 | _cachedClipComponentNone = null; 574 | _cachedRootComponent = null; 575 | } 576 | 577 | @:noCompletion 578 | private function cacheScreenPos() { 579 | if (_cachedScreenX != null && _cachedScreenY != null) { 580 | return; 581 | } 582 | 583 | var c:Component = cast(this, Component); 584 | var last:Component = null; 585 | var xpos:Float = 0; 586 | var ypos:Float = 0; 587 | while (c != null) { 588 | xpos += c.left; 589 | ypos += c.top; 590 | if (c.componentClipRect != null) { 591 | xpos -= c.componentClipRect.left; 592 | ypos -= c.componentClipRect.top; 593 | } 594 | last = c; 595 | c = c.parentComponent; 596 | } 597 | 598 | if (last != null && last.parent != null) { // UI might have been added deep in a heaps hierachy, so lets get the _real_ screen pos 599 | var o = last.parent; 600 | while (o != null) { 601 | xpos += o.x; 602 | ypos += o.y; 603 | o = o.parent; 604 | } 605 | } 606 | 607 | xpos *= Toolkit.scaleX; 608 | ypos *= Toolkit.scaleY; 609 | 610 | if (Toolkit.scaleX != 1) { 611 | xpos -= last.left; 612 | } 613 | if (Toolkit.scaleY != 1) { 614 | ypos -= last.top; 615 | } 616 | 617 | _cachedScreenX = xpos; 618 | _cachedScreenY = ypos; 619 | } 620 | 621 | @:noCompletion 622 | private var screenX(get, null):Float; 623 | @:noCompletion 624 | private function get_screenX():Float { 625 | cacheScreenPos(); 626 | return _cachedScreenX; 627 | } 628 | 629 | @:noCompletion 630 | private var screenY(get, null):Float; 631 | @:noCompletion 632 | private function get_screenY():Float { 633 | cacheScreenPos(); 634 | return _cachedScreenY; 635 | } 636 | 637 | @:noCompletion 638 | private function findRootComponent():Component { 639 | if (_cachedRootComponent != null) { 640 | return _cachedRootComponent; 641 | } 642 | 643 | var c:Component = cast(this, Component); 644 | while (c.parentComponent != null) { 645 | c = c.parentComponent; 646 | } 647 | 648 | _cachedRootComponent = c; 649 | 650 | return c; 651 | } 652 | 653 | @:noCompletion 654 | private function isRootComponent():Bool { 655 | return (findRootComponent() == this); 656 | } 657 | 658 | @:noCompletion 659 | private function findClipComponent():Component { 660 | if (_cachedClipComponent != null) { 661 | return _cachedClipComponent; 662 | } else if (_cachedClipComponentNone == true) { 663 | return null; 664 | } 665 | 666 | var c:Component = cast(this, Component); 667 | var clip:Component = null; 668 | while (c != null) { 669 | if (c.componentClipRect != null) { 670 | clip = c; 671 | break; 672 | } 673 | c = c.parentComponent; 674 | } 675 | 676 | _cachedClipComponent = clip; 677 | if (clip == null) { 678 | _cachedClipComponentNone = true; 679 | } 680 | 681 | return clip; 682 | } 683 | 684 | @:noCompletion 685 | @:access(haxe.ui.core.Component) 686 | private function inBounds(x:Float, y:Float):Bool { 687 | if (cast(this, Component).hidden == true) { 688 | return false; 689 | } 690 | 691 | if (isOnScreen() == false) { 692 | return false; 693 | } 694 | 695 | x *= Toolkit.scaleX; 696 | y *= Toolkit.scaleY; 697 | var b:Bool = false; 698 | var sx = screenX; 699 | var sy = screenY; 700 | var cx = this.width * Toolkit.scaleX; 701 | var cy = this.height * Toolkit.scaleY; 702 | 703 | if (x >= sx && y >= sy && x <= sx + cx && y <= sy + cy) { 704 | b = true; 705 | } 706 | 707 | // let make sure its in the clip rect too 708 | if (b == true) { 709 | var clip:Component = findClipComponent(); 710 | if (clip != null) { 711 | b = false; 712 | var sx = (clip.screenX + (clip.componentClipRect.left * Toolkit.scaleX)); 713 | var sy = (clip.screenY + (clip.componentClipRect.top * Toolkit.scaleY)); 714 | var cx = clip.componentClipRect.width * Toolkit.scaleX; 715 | var cy = clip.componentClipRect.height * Toolkit.scaleY; 716 | if (x >= sx && y >= sy && x <= sx + cx && y <= sy + cy) { 717 | b = true; 718 | } 719 | } 720 | } 721 | return b; 722 | } 723 | 724 | @:noCompletion 725 | private function isEventRelevant(children:Array, eventType:String):Bool { 726 | var relevant = false; 727 | for (c in children) { 728 | if (c == this) { 729 | relevant = true; 730 | } 731 | if (c.parentComponent == null) { 732 | break; 733 | } 734 | } 735 | 736 | return relevant; 737 | } 738 | 739 | @:noCompletion 740 | private function getComponentsAtPoint(x:Float, y:Float, reverse:Bool = false):Array { 741 | var array:Array = new Array(); 742 | for (r in Screen.instance.rootComponents) { 743 | findChildrenAtPoint(r, x, y, array); 744 | } 745 | 746 | if (reverse == true) { 747 | array.reverse(); 748 | } 749 | 750 | return array; 751 | } 752 | 753 | @:noCompletion 754 | private function findChildrenAtPoint(child:Component, x:Float, y:Float, array:Array) { 755 | if (child.inBounds(x, y) == true) { 756 | array.push(child); 757 | } 758 | for (c in child.childComponents) { 759 | findChildrenAtPoint(c, x, y, array); 760 | } 761 | } 762 | 763 | @:noCompletion 764 | public function hasChildRecursive(parent:Component, child:Component):Bool { 765 | if (parent == child) { 766 | return true; 767 | } 768 | var r = false; 769 | for (t in parent.childComponents) { 770 | if (t == child) { 771 | r = true; 772 | break; 773 | } 774 | 775 | r = hasChildRecursive(t, child); 776 | if (r == true) { 777 | break; 778 | } 779 | } 780 | 781 | return r; 782 | } 783 | 784 | @:noCompletion 785 | private override function sync(ctx:RenderContext) { 786 | var changed = posChanged; 787 | // if .x/.y property is access directly, we still want to honour it, so we will set haxeui's 788 | // .left/.top to keep them on sync, but only if the components position isnt already invalid 789 | // (which would mean this has come from a haxeui validation cycle) 790 | if (changed == true && isComponentInvalid(InvalidationFlags.POSITION) == false && _maskGraphics == null) { 791 | if (this.x != this.left) { 792 | this.left = this.x; 793 | } 794 | if (this.y != this.top) { 795 | this.top = this.y; 796 | } 797 | } 798 | super.sync(ctx); 799 | clearCaches(); 800 | } 801 | 802 | @:noCompletion 803 | private override function onAdd() { 804 | super.onAdd(); 805 | if (this.parentComponent == null && Screen.instance.rootComponents.indexOf(cast this) == -1) { 806 | Screen.instance.addComponent(cast this); 807 | } 808 | cast(this, Component).ready(); 809 | } 810 | 811 | @:noCompletion 812 | private override function onRemove() { 813 | if (_deallocate == true) { 814 | _disposed = true; 815 | super.onRemove(); 816 | } 817 | if (this.parentComponent == null && Screen.instance.rootComponents.indexOf(cast this) != -1) { 818 | Screen.instance.removeComponent(cast this, _deallocate); 819 | } 820 | } 821 | 822 | @:noCompletion 823 | private var lastMouseX:Float = -1; 824 | @:noCompletion 825 | private var lastMouseY:Float = -1; 826 | 827 | // For doubleclick detection 828 | @:noCompletion 829 | private var _lastClickTime:Float = 0; 830 | @:noCompletion 831 | private var _lastClickTimeDiff:Float = MathUtil.MAX_INT; 832 | @:noCompletion 833 | private var _lastClickX:Float = -1; 834 | @:noCompletion 835 | private var _lastClickY:Float = -1; 836 | 837 | @:noCompletion 838 | private function calcCursor():String { 839 | var c = null; 840 | var p = this; 841 | while (p != null) { 842 | if (p.style != null && p.style.cursor != null) { 843 | c = p.style.cursor; 844 | break; 845 | } 846 | p = p.parentComponent; 847 | } 848 | return c; 849 | } 850 | 851 | @:noCompletion 852 | private function findScene():Scene { 853 | if (this.getScene() != null) { 854 | return this.getScene(); 855 | } 856 | return Screen.instance.scene; 857 | } 858 | 859 | @:noCompletion 860 | private function findCamera():Camera { 861 | var scene = findScene(); 862 | if (scene == null) { 863 | return null; 864 | } 865 | 866 | if (scene.interactiveCamera != null) { 867 | return scene.interactiveCamera; 868 | } 869 | return scene.camera; 870 | } 871 | 872 | @:noCompletion 873 | private var _h2dPoint = new Point(); // we'll just reuse the same point rather than creating new ones 874 | @:noCompletion 875 | private function eventToCamera(event:MouseEvent) { 876 | _h2dPoint.x = event.screenX; 877 | _h2dPoint.y = event.screenY; 878 | var camera = findCamera(); 879 | if (camera != null) { 880 | camera.screenToCamera(_h2dPoint); 881 | } 882 | event.screenX = _h2dPoint.x / Toolkit.scaleX; 883 | event.screenY = _h2dPoint.y / Toolkit.scaleY; 884 | } 885 | 886 | @:noCompletion 887 | private var _mouseOverFlag:Bool = false; 888 | @:noCompletion 889 | private function __onMouseMove(event:MouseEvent) { 890 | eventToCamera(event); 891 | 892 | var x = event.screenX; 893 | var y = event.screenY; 894 | lastMouseX = x; 895 | lastMouseY = y; 896 | 897 | var i = inBounds(x, y); 898 | if (i == true) { 899 | if (_mouseOverFlag && hasComponentOver(cast this, x, y) == true) { 900 | _mouseOverFlag = false; 901 | var fn:UIEvent->Void = _eventMap.get(haxe.ui.events.MouseEvent.MOUSE_OUT); 902 | if (fn != null) { 903 | var mouseEvent = new haxe.ui.events.MouseEvent(haxe.ui.events.MouseEvent.MOUSE_OUT); 904 | mouseEvent.screenX = x; 905 | mouseEvent.screenY = y; 906 | fn(mouseEvent); 907 | event.canceled = mouseEvent.canceled; 908 | } 909 | return; 910 | } 911 | 912 | if (isEventRelevant(getComponentsAtPoint(x, y, true), MouseEvent.MOUSE_OVER)) { 913 | if (isInteractiveAbove(x, y)) { 914 | return; 915 | } 916 | 917 | var cursor = calcCursor(); 918 | if (cursor != null) { 919 | Screen.instance.setCursor(cursor); 920 | } 921 | } 922 | 923 | var fn:UIEvent->Void = _eventMap.get(haxe.ui.events.MouseEvent.MOUSE_MOVE); 924 | if (fn != null) { 925 | var mouseEvent = new haxe.ui.events.MouseEvent(haxe.ui.events.MouseEvent.MOUSE_MOVE); 926 | mouseEvent.screenX = x; 927 | mouseEvent.screenY = y; 928 | fn(mouseEvent); 929 | event.canceled = mouseEvent.canceled; 930 | } 931 | } 932 | 933 | if (i == true && _mouseOverFlag == false) { 934 | if (hasComponentOver(cast this, x, y) == true) { 935 | return; 936 | } 937 | 938 | if (isEventRelevant(getComponentsAtPoint(x, y, true), MouseEvent.MOUSE_OVER)) { 939 | _mouseOverFlag = true; 940 | var fn:UIEvent->Void = _eventMap.get(haxe.ui.events.MouseEvent.MOUSE_OVER); 941 | if (fn != null) { 942 | var mouseEvent = new haxe.ui.events.MouseEvent(haxe.ui.events.MouseEvent.MOUSE_OVER); 943 | mouseEvent.screenX = x; 944 | mouseEvent.screenY = y; 945 | fn(mouseEvent); 946 | event.canceled = mouseEvent.canceled; 947 | } 948 | } 949 | } else if (i == false && _mouseOverFlag == true) { 950 | _mouseOverFlag = false; 951 | Screen.instance.setCursor("default"); 952 | var fn:UIEvent->Void = _eventMap.get(haxe.ui.events.MouseEvent.MOUSE_OUT); 953 | if (fn != null) { 954 | var mouseEvent = new haxe.ui.events.MouseEvent(haxe.ui.events.MouseEvent.MOUSE_OUT); 955 | mouseEvent.screenX = x; 956 | mouseEvent.screenY = y; 957 | fn(mouseEvent); 958 | event.canceled = mouseEvent.canceled; 959 | } 960 | } 961 | } 962 | 963 | @:noCompletion 964 | private var _mouseDownFlag:Bool = false; 965 | @:noCompletion 966 | private var _mouseDownButton:Int = -1; 967 | @:noCompletion 968 | private function __onMouseDown(event:MouseEvent) { 969 | eventToCamera(event); 970 | 971 | var button:Int = event.data; 972 | var x = event.screenX; 973 | var y = event.screenY; 974 | lastMouseX = x; 975 | lastMouseY = y; 976 | var i = inBounds(x, y); 977 | if (i == true && _mouseDownFlag == false) { 978 | /* 979 | if (hasComponentOver(cast this, x, y) == true) { 980 | return; 981 | } 982 | */ 983 | if (isEventRelevant(getComponentsAtPoint(x, y, true), MouseEvent.MOUSE_DOWN)) { 984 | if (isInteractiveAbove(x, y)) { 985 | return; 986 | } 987 | 988 | _mouseDownFlag = true; 989 | 990 | /* TODO: feels hacky (ill-conceived?) 991 | if (this.style != null && (this.style.cursor == "row-resize" || this.style.cursor == "col-resize")) { 992 | Screen.instance.lockCursor(); 993 | } 994 | */ 995 | 996 | _mouseDownButton = button; 997 | var type = switch(button) { 998 | case 0: haxe.ui.events.MouseEvent.MOUSE_DOWN; 999 | case 1: haxe.ui.events.MouseEvent.RIGHT_MOUSE_DOWN; 1000 | case 2: haxe.ui.events.MouseEvent.MIDDLE_MOUSE_DOWN; 1001 | case _: haxe.ui.events.MouseEvent.MOUSE_DOWN; 1002 | } 1003 | var fn:UIEvent->Void = _eventMap.get(type); 1004 | if (fn != null) { 1005 | var mouseEvent = new haxe.ui.events.MouseEvent(type); 1006 | mouseEvent.data = button; 1007 | mouseEvent.screenX = x; 1008 | mouseEvent.screenY = y; 1009 | fn(mouseEvent); 1010 | event.canceled = mouseEvent.canceled; 1011 | } 1012 | } 1013 | } 1014 | } 1015 | 1016 | @:noCompletion 1017 | private function __onMouseUp(event:MouseEvent) { 1018 | eventToCamera(event); 1019 | 1020 | var button:Int = _mouseDownButton; 1021 | var x = event.screenX; 1022 | var y = event.screenY; 1023 | 1024 | lastMouseX = x; 1025 | lastMouseY = y; 1026 | 1027 | var i = inBounds(x, y); 1028 | if (i == true) { 1029 | /* 1030 | if (hasComponentOver(cast this, x, y) == true) { 1031 | return; 1032 | } 1033 | */ 1034 | 1035 | if (_mouseDownFlag == true) { 1036 | var type = switch(button) { 1037 | case 0: haxe.ui.events.MouseEvent.CLICK; 1038 | case 1: haxe.ui.events.MouseEvent.RIGHT_CLICK; 1039 | case 2: haxe.ui.events.MouseEvent.MIDDLE_CLICK; 1040 | case _: haxe.ui.events.MouseEvent.CLICK; 1041 | } 1042 | var fn:UIEvent->Void = _eventMap.get(type); 1043 | if (fn != null) { 1044 | var mouseEvent = new haxe.ui.events.MouseEvent(type); 1045 | mouseEvent.data = button; 1046 | mouseEvent.screenX = x; 1047 | mouseEvent.screenY = y; 1048 | Toolkit.callLater(function() { 1049 | fn(mouseEvent); 1050 | event.canceled = mouseEvent.canceled; 1051 | }); 1052 | } 1053 | 1054 | if (type == haxe.ui.events.MouseEvent.CLICK) { 1055 | _lastClickTimeDiff = Timer.stamp() - _lastClickTime; 1056 | _lastClickTime = Timer.stamp(); 1057 | if (_lastClickTimeDiff >= 0.5) { // 0.5 seconds 1058 | _lastClickX = x; 1059 | _lastClickY = y; 1060 | } 1061 | } 1062 | } 1063 | 1064 | /* TODO: feels hacky (ill-conceived?) 1065 | if (_mouseDownFlag && this.style != null) { 1066 | Screen.instance.unlockCursor(); 1067 | Screen.instance.setCursor(calcCursor()); 1068 | } 1069 | */ 1070 | 1071 | _mouseDownFlag = false; 1072 | var type = switch(button) { 1073 | case 0: haxe.ui.events.MouseEvent.MOUSE_UP; 1074 | case 1: haxe.ui.events.MouseEvent.RIGHT_MOUSE_UP; 1075 | case 2: haxe.ui.events.MouseEvent.MIDDLE_MOUSE_UP; 1076 | case _: haxe.ui.events.MouseEvent.MOUSE_UP; 1077 | } 1078 | var fn:UIEvent->Void = _eventMap.get(type); 1079 | if (fn != null) { 1080 | var mouseEvent = new haxe.ui.events.MouseEvent(type); 1081 | mouseEvent.data = button; 1082 | mouseEvent.screenX = x; 1083 | mouseEvent.screenY = y; 1084 | fn(mouseEvent); 1085 | event.canceled = mouseEvent.canceled; 1086 | } 1087 | } else { 1088 | /* TODO: feels hacky (ill-conceived?) 1089 | if (_mouseDownFlag) { 1090 | Screen.instance.unlockCursor(); 1091 | Screen.instance.setCursor("default"); 1092 | } 1093 | */ 1094 | } 1095 | _mouseDownFlag = false; 1096 | } 1097 | 1098 | @:noCompletion 1099 | private function __onDoubleClick(event:MouseEvent) { 1100 | eventToCamera(event); 1101 | 1102 | var button:Int = _mouseDownButton; 1103 | var x = event.screenX; 1104 | var y = event.screenY; 1105 | 1106 | lastMouseX = x; 1107 | lastMouseY = y; 1108 | var i = inBounds(x, y); 1109 | if (i == true && button == 0) { 1110 | /* 1111 | if (hasComponentOver(cast this, x, y) == true) { 1112 | return; 1113 | } 1114 | */ 1115 | 1116 | _mouseDownFlag = false; 1117 | var mouseDelta:Float = MathUtil.distance(x, y, _lastClickX, _lastClickY); 1118 | if (_lastClickTimeDiff < 0.5 && mouseDelta < 5) { // 0.5 seconds 1119 | var type = haxe.ui.events.MouseEvent.DBL_CLICK; 1120 | var fn:UIEvent->Void = _eventMap.get(type); 1121 | if (fn != null) { 1122 | var mouseEvent = new haxe.ui.events.MouseEvent(type); 1123 | mouseEvent.data = button; 1124 | mouseEvent.screenX = x; 1125 | mouseEvent.screenY = y; 1126 | fn(mouseEvent); 1127 | event.canceled = mouseEvent.canceled; 1128 | } 1129 | } 1130 | } 1131 | _mouseDownFlag = false; 1132 | } 1133 | 1134 | @:noCompletion 1135 | private function __onMouseWheel(event:MouseEvent) { 1136 | eventToCamera(event); 1137 | 1138 | var delta = event.delta; 1139 | var fn = _eventMap.get(MouseEvent.MOUSE_WHEEL); 1140 | 1141 | if (fn == null) { 1142 | return; 1143 | } 1144 | 1145 | if (!inBounds(lastMouseX, lastMouseY)) { 1146 | return; 1147 | } 1148 | 1149 | if (isInteractiveAbove(lastMouseX, lastMouseY)) { 1150 | return; 1151 | } 1152 | 1153 | var mouseEvent = new MouseEvent(MouseEvent.MOUSE_WHEEL); 1154 | mouseEvent.screenX = lastMouseX; 1155 | mouseEvent.screenY = lastMouseY; 1156 | mouseEvent.delta = Math.max(-1, Math.min(1, -delta)); 1157 | fn(mouseEvent); 1158 | event.canceled = mouseEvent.canceled; 1159 | } 1160 | 1161 | //*********************************************************************************************************** 1162 | // Helpers 1163 | //*********************************************************************************************************** 1164 | @:noCompletion 1165 | private function hasComponentOver(ref:Component, x:Float, y:Float):Bool { 1166 | var array:Array = getComponentsAtPoint(x, y); 1167 | if (array.length == 0) { 1168 | return false; 1169 | } 1170 | 1171 | return !hasChildRecursive(cast ref, cast array[array.length - 1]); 1172 | } 1173 | 1174 | @:noCompletion 1175 | private override function set_visible(value:Bool):Bool { 1176 | if (value == this.visible) { 1177 | return value; 1178 | } 1179 | super.visible = value; 1180 | cast(this, Component).hidden = !value; 1181 | return value; 1182 | } 1183 | 1184 | @:noCompletion 1185 | private function calcObjectIndex(obj:Object):Int { 1186 | var n = 0; 1187 | while (obj.parent != null) { 1188 | n++; 1189 | if (obj.parent == obj.getScene()) { 1190 | break; 1191 | } 1192 | obj = obj.parent; 1193 | } 1194 | return obj.getScene().getChildIndex(obj); 1195 | } 1196 | 1197 | @:noCompletion 1198 | private function isOnScreen() { 1199 | var obj:Object = this; 1200 | while (obj.parent != null) { 1201 | if (obj.visible == false) { 1202 | return false; 1203 | } 1204 | obj = obj.parent; 1205 | if (obj == obj.getScene()) { 1206 | break; 1207 | } 1208 | } 1209 | return true; 1210 | } 1211 | 1212 | @:noCompletion 1213 | private function isInteractiveAbove(x:Float, y:Float) { 1214 | var scene = this.getScene(); 1215 | if (scene != null) { 1216 | var interactive = scene.getInteractive(x, y); 1217 | if (interactive != null) { 1218 | var n1 = calcObjectIndex(interactive); 1219 | var n2 = calcObjectIndex(this); 1220 | if (n1 > n2) { 1221 | hxd.System.setNativeCursor(interactive.cursor); 1222 | return true; 1223 | } 1224 | } 1225 | } 1226 | 1227 | return false; 1228 | } 1229 | } 1230 | -------------------------------------------------------------------------------- /haxe/ui/backend/ComponentSurface.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | typedef ComponentSurface = h2d.Object; 4 | -------------------------------------------------------------------------------- /haxe/ui/backend/EventImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import haxe.ui.events.UIEvent; 4 | 5 | @:allow(haxe.ui.backend.ComponentImpl) 6 | @:allow(haxe.ui.backend.ScreenImpl) 7 | class EventImpl extends EventBase { 8 | private var _originalEvent:hxd.Event; 9 | 10 | public override function cancel() { 11 | if (_originalEvent != null) { 12 | _originalEvent.cancel = true; 13 | _originalEvent.propagate = false; 14 | } 15 | } 16 | 17 | private override function postClone(event:UIEvent) { 18 | event._originalEvent = this._originalEvent; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /haxe/ui/backend/FontData.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | typedef FontData = hxd.res.BitmapFont; -------------------------------------------------------------------------------- /haxe/ui/backend/ImageData.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | typedef ImageData = hxd.BitmapData; 4 | -------------------------------------------------------------------------------- /haxe/ui/backend/ImageDisplayImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | import haxe.ui.Toolkit; 3 | 4 | class ImageDisplayImpl extends ImageBase { 5 | public var sprite:h2d.Bitmap; 6 | 7 | public function new() { 8 | super(); 9 | sprite = new h2d.Bitmap(); 10 | } 11 | 12 | private override function validateData() { 13 | if (_imageInfo != null) { 14 | sprite.tile = h2d.Tile.fromBitmap(_imageInfo.data); 15 | } else { 16 | sprite.tile.dispose(); 17 | sprite.tile = null; 18 | } 19 | } 20 | 21 | private override function validatePosition() { 22 | if (sprite.x != _left) { 23 | sprite.x = _left; 24 | } 25 | 26 | if (sprite.y != _top) { 27 | sprite.y = _top; 28 | } 29 | } 30 | 31 | private override function validateDisplay() { 32 | if (sprite.tile != null) { 33 | var scaleX:Float = (_imageWidth / sprite.tile.width); 34 | if (sprite.scaleX != scaleX) { 35 | sprite.scaleX = scaleX; 36 | } 37 | 38 | var scaleY:Float = (_imageHeight / sprite.tile.height); 39 | if (sprite.scaleY != scaleY) { 40 | sprite.scaleY = scaleY; 41 | } 42 | 43 | sprite.smooth = false;//scaleX != Toolkit.scaleX || scaleY != Toolkit.scaleY; 44 | } 45 | } 46 | 47 | public override function dispose() { 48 | if (sprite.tile != null) { 49 | sprite.tile.dispose(); 50 | sprite.tile = null; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /haxe/ui/backend/ImageSurface.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | typedef ImageSurface = h2d.Bitmap; -------------------------------------------------------------------------------- /haxe/ui/backend/OpenFileDialogImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import haxe.ui.containers.dialogs.Dialogs.SelectedFileInfo; 4 | import haxe.ui.core.Platform; 5 | 6 | using StringTools; 7 | 8 | class OpenFileDialogImpl extends OpenFileDialogBase { 9 | #if hl 10 | 11 | public override function show() { 12 | if (Platform.instance.isWindows) { 13 | var title = options.title; 14 | if (title == null) { 15 | title = "Open File"; 16 | } 17 | var nativeOptions:hl.UI.FileOptions = { } 18 | nativeOptions.title = title; 19 | nativeOptions.filters = buildFilters(); 20 | 21 | var allowTimeout = hxd.System.allowTimeout; 22 | hxd.System.allowTimeout = false; 23 | var file = hl.UI.loadFile(nativeOptions); 24 | hxd.System.allowTimeout = allowTimeout; 25 | if (file != null) { 26 | var infos:Array = []; 27 | infos.push({ 28 | name: haxe.io.Path.withoutDirectory(file), 29 | fullPath: file, 30 | isBinary: false 31 | }); 32 | 33 | if (options.readContents == true) { 34 | for (info in infos) { 35 | if (options.readAsBinary) { 36 | info.isBinary = true; 37 | info.bytes = sys.io.File.getBytes(info.fullPath); 38 | } else { 39 | info.isBinary = false; 40 | info.text = sys.io.File.getContent(info.fullPath); 41 | } 42 | } 43 | } 44 | 45 | dialogConfirmed(infos); 46 | } else { 47 | dialogCancelled(); 48 | } 49 | } else { 50 | super.show(); 51 | } 52 | } 53 | 54 | private function buildFilters():Array<{name:String, exts:Array}> { 55 | var filters = null; 56 | if (options.extensions != null) { 57 | filters = []; 58 | for (e in options.extensions) { 59 | var ext = e.extension; 60 | ext = ext.trim(); 61 | if (ext.length == 0) { 62 | continue; 63 | } 64 | var single = e.label; 65 | var parts = ext.split(","); 66 | var finalParts = []; 67 | for (p in parts) { 68 | p = p.trim(); 69 | if (p.length == 0) { 70 | continue; 71 | } 72 | finalParts.push(p); 73 | } 74 | single += " (" + finalParts.join(", ") + ")"; 75 | filters.push({name: single, exts: finalParts}); 76 | } 77 | } 78 | return filters; 79 | } 80 | 81 | #elseif js 82 | 83 | private var _fileSelector:haxe.ui.util.html5.FileSelector = new haxe.ui.util.html5.FileSelector(); 84 | 85 | public override function show() { 86 | var readMode = haxe.ui.util.html5.FileSelector.ReadMode.None; 87 | if (options.readContents == true) { 88 | if (options.readAsBinary == false) { 89 | readMode = haxe.ui.util.html5.FileSelector.ReadMode.Text; 90 | } else { 91 | readMode = haxe.ui.util.html5.FileSelector.ReadMode.Binary; 92 | } 93 | } 94 | _fileSelector.selectFile(onFileSelected, readMode, options.multiple, options.extensions); 95 | } 96 | 97 | private function onFileSelected(cancelled:Bool, files:Array) { 98 | if (cancelled == false) { 99 | dialogConfirmed(files); 100 | } else { 101 | dialogCancelled(); 102 | } 103 | } 104 | 105 | #end 106 | } -------------------------------------------------------------------------------- /haxe/ui/backend/PlatformImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | class PlatformImpl extends PlatformBase { 4 | } -------------------------------------------------------------------------------- /haxe/ui/backend/SaveFileDialogImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import haxe.ui.core.Platform; 4 | 5 | using StringTools; 6 | 7 | class SaveFileDialogImpl extends SaveFileDialogBase { 8 | #if hl 9 | 10 | public override function show() { 11 | if (fileInfo == null || (fileInfo.text == null && fileInfo.bytes == null)) { 12 | throw "Nothing to write"; 13 | } 14 | 15 | if (Platform.instance.isWindows) { 16 | var title = options.title; 17 | if (title == null) { 18 | title = "Save File"; 19 | } 20 | var nativeOptions:hl.UI.FileOptions = { } 21 | nativeOptions.title = title; 22 | nativeOptions.fileName = fileInfo.name; 23 | nativeOptions.filters = buildFilters(); 24 | 25 | var allowTimeout = hxd.System.allowTimeout; 26 | hxd.System.allowTimeout = false; 27 | var file = hl.UI.saveFile(nativeOptions); 28 | hxd.System.allowTimeout = allowTimeout; 29 | if (file != null) { 30 | fullPath = file; 31 | if (fileInfo.text != null) { 32 | sys.io.File.saveContent(fullPath, fileInfo.text); 33 | } else if (fileInfo.bytes != null) { 34 | sys.io.File.saveBytes(fullPath, fileInfo.bytes); 35 | } 36 | dialogConfirmed(); 37 | } else { 38 | dialogCancelled(); 39 | } 40 | } else { 41 | super.show(); 42 | } 43 | } 44 | 45 | 46 | private function buildFilters():Array<{name:String, exts:Array}> { 47 | var filters = null; 48 | if (options.extensions != null) { 49 | filters = []; 50 | for (e in options.extensions) { 51 | var ext = e.extension; 52 | ext = ext.trim(); 53 | if (ext.length == 0) { 54 | continue; 55 | } 56 | var single = e.label; 57 | var parts = ext.split(","); 58 | var finalParts = []; 59 | for (p in parts) { 60 | p = p.trim(); 61 | if (p.length == 0) { 62 | continue; 63 | } 64 | finalParts.push(p); 65 | } 66 | single += " (" + finalParts.join(", ") + ")"; 67 | filters.push({name: single, exts: finalParts}); 68 | } 69 | } 70 | return filters; 71 | } 72 | 73 | #elseif js 74 | 75 | private var _fileSaver:haxe.ui.util.html5.FileSaver = new haxe.ui.util.html5.FileSaver(); 76 | 77 | public override function show() { 78 | if (fileInfo == null || (fileInfo.text == null && fileInfo.bytes == null)) { 79 | throw "Nothing to write"; 80 | } 81 | 82 | if (fileInfo.text != null) { 83 | _fileSaver.saveText(fileInfo.name, fileInfo.text, onSaveResult); 84 | } else if (fileInfo.bytes != null) { 85 | _fileSaver.saveBinary(fileInfo.name, fileInfo.bytes, onSaveResult); 86 | } 87 | } 88 | 89 | private function onSaveResult(r:Bool) { 90 | if (r == true) { 91 | dialogConfirmed(); 92 | } else { 93 | dialogCancelled(); 94 | } 95 | } 96 | 97 | #end 98 | } 99 | -------------------------------------------------------------------------------- /haxe/ui/backend/ScreenImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import h2d.Object; 4 | import h2d.Scene; 5 | import haxe.ui.Toolkit; 6 | import haxe.ui.backend.heaps.EventMapper; 7 | import haxe.ui.backend.heaps.KeyboardHelper; 8 | import haxe.ui.backend.heaps.MouseHelper; 9 | import haxe.ui.backend.heaps.ScreenUtils; 10 | import haxe.ui.core.Component; 11 | import haxe.ui.events.KeyboardEvent; 12 | import haxe.ui.events.MouseEvent; 13 | import haxe.ui.events.UIEvent; 14 | import hxd.Window; 15 | 16 | class ScreenImpl extends ScreenBase { 17 | private var _mapping:MapVoid>; 18 | 19 | public function new() { 20 | _mapping = new MapVoid>(); 21 | addResizeListener(); 22 | } 23 | 24 | private var _resizeListenerAdded:Bool = false; 25 | private function addResizeListener() { 26 | if (_resizeListenerAdded == true) { 27 | return; 28 | } 29 | 30 | if (Window.getInstance() != null) { 31 | Window.getInstance().addResizeEvent(onWindowResize); 32 | _resizeListenerAdded = true; 33 | } 34 | } 35 | 36 | private var _updateCallbackAdded:Bool = false; 37 | private function addUpdateCallback() { 38 | if (_updateCallbackAdded == true) { 39 | return; 40 | } 41 | 42 | if (options == null || options.manualUpdate == null || options.manualUpdate == false) { 43 | _updateCallbackAdded = true; 44 | MainLoop.add(BackendImpl.update); 45 | } 46 | } 47 | 48 | private function onWindowResize() { 49 | resizeRootComponents(); 50 | if (_mapping.exists(UIEvent.RESIZE)) { 51 | _mapping.get(UIEvent.RESIZE)(new UIEvent(UIEvent.RESIZE)); 52 | } 53 | } 54 | 55 | private var _root:Object = null; 56 | public var root(get, set):Object; 57 | private function get_root():Object { 58 | if (_root != null) { 59 | return _root; 60 | } 61 | 62 | if (options == null) { 63 | return null; 64 | } 65 | return options.root; 66 | } 67 | private function set_root(value:Object):Object { 68 | _root = value; 69 | return value; 70 | } 71 | 72 | public var scene(get, null):Scene; 73 | private function get_scene():Scene { 74 | if (root == null) { 75 | return null; 76 | } 77 | return root.getScene(); 78 | } 79 | 80 | 81 | private override function set_options(value:ToolkitOptions):ToolkitOptions { 82 | super.set_options(value); 83 | addUpdateCallback(); 84 | return value; 85 | } 86 | 87 | private override function get_width():Float { 88 | return Window.getInstance().width / Toolkit.scaleX; 89 | } 90 | 91 | private override function get_height():Float { 92 | return Window.getInstance().height / Toolkit.scaleY; 93 | } 94 | 95 | private override function get_dpi():Float { 96 | return ScreenUtils.dpi; 97 | } 98 | 99 | private override function get_isRetina():Bool { 100 | return ScreenUtils.isRetinaDisplay(); 101 | } 102 | 103 | private var cursorLocked:Bool = false; 104 | public function setCursor(cursor:String, lock=false) { 105 | if (cursorLocked) { 106 | return; 107 | } 108 | cursorLocked = lock; 109 | 110 | if (cursor == "pointer") { 111 | hxd.System.setNativeCursor(Button); 112 | } else if (cursor == "text") { 113 | hxd.System.setNativeCursor(TextInput); 114 | } else if (cursor == "col-resize") { 115 | hxd.System.setNativeCursor(Move); 116 | } else if (cursor == "row-resize") { 117 | hxd.System.setNativeCursor(Move); 118 | }else { 119 | hxd.System.setNativeCursor(Default); 120 | } 121 | } 122 | 123 | public function lockCursor() { 124 | cursorLocked = true; 125 | } 126 | public function unlockCursor() { 127 | cursorLocked = false; 128 | } 129 | 130 | public override function addComponent(component:Component):Component { 131 | rootComponents.push(component); 132 | component.scaleX = Toolkit.scaleX; 133 | component.scaleY = Toolkit.scaleY; 134 | if (component.parent == null || component.parent == root) { 135 | if (_removedComponents.indexOf(component) != -1) { 136 | if (root == null) { 137 | trace("WARNING: trying to add a component to a null root. Either set Screen.instance.root or specify one in Toolkit.init"); 138 | return component; 139 | } 140 | _removedComponents.remove(component); 141 | //rootScene.addChildAt(component, 0); 142 | component.visible = true; 143 | } else { 144 | if (root == null) { 145 | trace("WARNING: trying to add a component to a null root. Either set Screen.instance.root or specify one in Toolkit.init"); 146 | return component; 147 | } 148 | root.addChild(component); 149 | } 150 | } 151 | resizeComponent(component); 152 | return component; 153 | } 154 | 155 | private var _removedComponents:Array = []; // TODO: probably ill conceived 156 | public override function removeComponent(component:Component, dispose:Bool = true, invalidate:Bool = true):Component { 157 | rootComponents.remove(component); 158 | if (_removedComponents.indexOf(component) == -1) { 159 | if (root == null) { 160 | trace("WARNING: trying to remove a component to a null root. Either set Screen.instance.root or specify one in Toolkit.init"); 161 | return component; 162 | } 163 | //rootScene.removeChild(component); 164 | _removedComponents.push(component); 165 | component.visible = false; 166 | if (dispose == true && root != null) { 167 | root.removeChild(component); 168 | } 169 | 170 | if (@:privateAccess component.inBounds(MouseHelper.currentMouseX, MouseHelper.currentMouseY)) { 171 | setCursor(null); 172 | } 173 | } 174 | return component; 175 | } 176 | 177 | private override function handleSetComponentIndex(component:Component, index:Int) { 178 | if (root == null) { 179 | trace("WARNING: trying to set a component index in a null root. Either set Screen.instance.root or specify one in Toolkit.init"); 180 | return; 181 | } 182 | root.addChildAt(component, index); 183 | resizeComponent(component); 184 | } 185 | 186 | //*********************************************************************************************************** 187 | // Events 188 | //*********************************************************************************************************** 189 | private override function supportsEvent(type:String):Bool { 190 | if (type == UIEvent.RESIZE) { 191 | return true; 192 | } 193 | return EventMapper.HAXEUI_TO_HEAPS.get(type) != null; 194 | } 195 | 196 | private var _deferredEvents:Array<{type:String, listener:UIEvent->Void}> = null; 197 | private function mapDeferredEvents() { 198 | if (hxd.Window.getInstance() == null) { 199 | return; 200 | } 201 | 202 | if (_deferredEvents == null) { 203 | return; 204 | } 205 | 206 | addResizeListener(); 207 | while (_deferredEvents.length > 0) { 208 | var info = _deferredEvents.shift(); 209 | mapEvent(info.type, info.listener); 210 | if (_deferredEvents == null) { 211 | break; 212 | } 213 | } 214 | _deferredEvents = null; 215 | } 216 | 217 | private override function mapEvent(type:String, listener:UIEvent->Void) { 218 | if (hxd.Window.getInstance() == null) { 219 | if (_deferredEvents == null) { 220 | _deferredEvents = []; 221 | } 222 | _deferredEvents.push({ 223 | type: type, 224 | listener: listener 225 | }); 226 | return; 227 | } 228 | mapDeferredEvents(); 229 | 230 | switch (type) { 231 | case MouseEvent.MOUSE_MOVE: 232 | if (_mapping.exists(type) == false) { 233 | _mapping.set(type, listener); 234 | MouseHelper.notify(MouseEvent.MOUSE_MOVE, __onMouseMove); 235 | } 236 | case MouseEvent.MOUSE_DOWN: 237 | if (_mapping.exists(type) == false) { 238 | _mapping.set(type, listener); 239 | MouseHelper.notify(MouseEvent.MOUSE_DOWN, __onMouseDown); 240 | } 241 | case MouseEvent.MOUSE_UP: 242 | if (_mapping.exists(type) == false) { 243 | _mapping.set(type, listener); 244 | MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); 245 | } 246 | case KeyboardEvent.KEY_DOWN: 247 | if (_mapping.exists(type) == false) { 248 | _mapping.set(type, listener); 249 | KeyboardHelper.notify(KeyboardEvent.KEY_DOWN, __onKeyDown); 250 | } 251 | case KeyboardEvent.KEY_UP: 252 | if (_mapping.exists(type) == false) { 253 | _mapping.set(type, listener); 254 | KeyboardHelper.notify(KeyboardEvent.KEY_UP, __onKeyUp); 255 | } 256 | case UIEvent.RESIZE: 257 | if (_mapping.exists(type) == false) { 258 | _mapping.set(type, listener); 259 | addResizeListener(); 260 | } 261 | } 262 | } 263 | 264 | private override function unmapEvent(type:String, listener:UIEvent->Void) { 265 | switch (type) { 266 | case MouseEvent.MOUSE_MOVE: 267 | _mapping.remove(type); 268 | MouseHelper.remove(MouseEvent.MOUSE_MOVE, __onMouseMove); 269 | case MouseEvent.MOUSE_DOWN: 270 | _mapping.remove(type); 271 | MouseHelper.remove(MouseEvent.MOUSE_DOWN, __onMouseDown); 272 | case MouseEvent.MOUSE_UP: 273 | _mapping.remove(type); 274 | MouseHelper.remove(MouseEvent.MOUSE_UP, __onMouseUp); 275 | case KeyboardEvent.KEY_DOWN: 276 | _mapping.remove(type); 277 | KeyboardHelper.remove(KeyboardEvent.KEY_DOWN, __onKeyDown); 278 | case KeyboardEvent.KEY_UP: 279 | _mapping.remove(type); 280 | KeyboardHelper.remove(KeyboardEvent.KEY_UP, __onKeyUp); 281 | case UIEvent.RESIZE: 282 | _mapping.remove(type); 283 | } 284 | } 285 | 286 | private function __onMouseMove(event:MouseEvent) { 287 | var fn = _mapping.get(MouseEvent.MOUSE_MOVE); 288 | if (fn != null) { 289 | var mouseEvent = new MouseEvent(MouseEvent.MOUSE_MOVE); 290 | mouseEvent.screenX = event.screenX; 291 | mouseEvent.screenY = event.screenY; 292 | mouseEvent.buttonDown = event.data; 293 | fn(mouseEvent); 294 | } 295 | } 296 | 297 | private function __onMouseDown(event:MouseEvent) { 298 | var fn = _mapping.get(MouseEvent.MOUSE_DOWN); 299 | if (fn != null) { 300 | var mouseEvent = new MouseEvent(MouseEvent.MOUSE_DOWN); 301 | mouseEvent.screenX = event.screenX; 302 | mouseEvent.screenY = event.screenY; 303 | mouseEvent.buttonDown = event.data; 304 | fn(mouseEvent); 305 | } 306 | } 307 | 308 | private function __onMouseUp(event:MouseEvent) { 309 | var fn = _mapping.get(MouseEvent.MOUSE_UP); 310 | if (fn != null) { 311 | var mouseEvent = new MouseEvent(MouseEvent.MOUSE_UP); 312 | mouseEvent.screenX = event.screenX; 313 | mouseEvent.screenY = event.screenY; 314 | mouseEvent.buttonDown = event.data; 315 | fn(mouseEvent); 316 | } 317 | } 318 | 319 | private function __onKeyDown(event:KeyboardEvent) { 320 | var fn = _mapping.get(KeyboardEvent.KEY_DOWN); 321 | if (fn != null) { 322 | var keyboardEvent = new KeyboardEvent(KeyboardEvent.KEY_DOWN); 323 | keyboardEvent.keyCode = event.keyCode; 324 | keyboardEvent.shiftKey = event.shiftKey; 325 | keyboardEvent.ctrlKey = event.ctrlKey; 326 | keyboardEvent.altKey = event.altKey; 327 | fn(keyboardEvent); 328 | } 329 | } 330 | 331 | private function __onKeyUp(event:KeyboardEvent) { 332 | var fn = _mapping.get(KeyboardEvent.KEY_UP); 333 | if (fn != null) { 334 | var keyboardEvent = new KeyboardEvent(KeyboardEvent.KEY_UP); 335 | keyboardEvent.keyCode = event.keyCode; 336 | keyboardEvent.shiftKey = event.shiftKey; 337 | keyboardEvent.ctrlKey = event.ctrlKey; 338 | keyboardEvent.altKey = event.altKey; 339 | fn(keyboardEvent); 340 | } 341 | /* TODO? Not sure if its important 342 | var fn = _mapping.get(KeyboardEvent.KEY_PRESS); 343 | if (fn != null) { 344 | var keyboardEvent = new KeyboardEvent(KeyboardEvent.KEY_PRESS); 345 | keyboardEvent.keyCode = event.keyCode; 346 | fn(keyboardEvent); 347 | } 348 | */ 349 | } 350 | } -------------------------------------------------------------------------------- /haxe/ui/backend/TextDisplayImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import h2d.Font; 4 | import haxe.ui.Toolkit; 5 | import haxe.ui.backend.heaps.SDFFonts; 6 | 7 | class TextDisplayImpl extends TextBase { 8 | public var sprite:h2d.Text; 9 | 10 | // defaults 11 | public static var defaultFontSize:Int = 12; 12 | 13 | public var isDefaultFont:Bool = true; 14 | 15 | public function new() { 16 | super(); 17 | sprite = createText(); 18 | sprite.visible = false; 19 | Toolkit.callLater(function() { // lets avoid text apearing at 0,0 initially by showing it 1 frame later 20 | sprite.visible = true; 21 | }); 22 | } 23 | 24 | private override function validateData() { 25 | if (_text != null) { 26 | sprite.text = normalizeText(_text); 27 | } else if (_htmlText != null) { 28 | sprite.text = normalizeText(_htmlText); 29 | } 30 | 31 | } 32 | 33 | private var _currentFontData:FontData = null; 34 | private override function validateStyle():Bool { 35 | var measureTextRequired:Bool = false; 36 | 37 | var isBitmap = false; 38 | if (_fontInfo != null && _fontInfo.data != null) { 39 | if (_fontInfo.data != _currentFontData) { 40 | _currentFontData = _fontInfo.data; 41 | 42 | var font:Font = null; 43 | var sdfDetails = SDFFonts.get(_fontInfo.name); 44 | if (sdfDetails != null) { 45 | font = _currentFontData.toSdfFont(defaultFontSize, sdfDetails.channel, sdfDetails.alphaCutoff, sdfDetails.smoothing).clone(); 46 | } else { 47 | font = _currentFontData.toFont().clone(); 48 | isBitmap = true; 49 | } 50 | 51 | if (sprite.font != font) { 52 | sprite.font = font; 53 | measureTextRequired = true; 54 | } 55 | 56 | isDefaultFont = false; 57 | } 58 | } 59 | 60 | if (_textStyle != null) { 61 | var fontSizeValue = Std.int(_textStyle.fontSize); 62 | if (fontSizeValue <= 0) { 63 | fontSizeValue = Std.int(defaultFontSize); 64 | } 65 | 66 | var currentFontSize = sprite.font.size; 67 | if (currentFontSize < 0) { // no Math.fabs 68 | currentFontSize = -currentFontSize; 69 | } 70 | 71 | if (currentFontSize != fontSizeValue) { 72 | resizeFont(fontSizeValue, isBitmap); 73 | measureTextRequired = true; 74 | } 75 | 76 | if (_displayData.wordWrap != sprite.lineBreak) { 77 | sprite.lineBreak = _displayData.wordWrap; 78 | measureTextRequired = true; 79 | } 80 | 81 | var textAlign:h2d.Text.Align = getAlign(_textStyle.textAlign); 82 | if (sprite.textAlign != textAlign) { 83 | sprite.textAlign = textAlign; 84 | measureTextRequired = true; 85 | } 86 | 87 | if (sprite.textColor != _textStyle.color) { 88 | sprite.textColor = _textStyle.color; 89 | } 90 | } 91 | 92 | return measureTextRequired; 93 | } 94 | 95 | private function resizeFont(fontSizeValue:Int, isBitmap:Bool) { 96 | var temp = sprite.font.clone(); 97 | sprite.font = null; 98 | if (isBitmap) { 99 | temp.resizeTo(-fontSizeValue); 100 | } else { 101 | if (temp == hxd.res.DefaultFont.get()) { 102 | temp = hxd.res.DefaultFont.get().clone(); 103 | } 104 | temp.resizeTo(fontSizeValue); 105 | } 106 | sprite.font = temp; 107 | temp = null; 108 | } 109 | 110 | private override function validatePosition() { 111 | if (autoWidth == true && sprite.textAlign == h2d.Text.Align.Center) { 112 | sprite.x = (_left) + (_width / 2); 113 | } else { 114 | sprite.x = (_left); 115 | } 116 | 117 | var offset:Float = 0; 118 | if (!isDefaultFont) { 119 | var currentFontSize = sprite.font.size; 120 | if (currentFontSize < 0) { // no Math.fabs 121 | currentFontSize = -currentFontSize; 122 | } 123 | offset = ((currentFontSize - sprite.font.baseLine) / 2); 124 | } 125 | 126 | sprite.y = (_top) + offset; 127 | } 128 | 129 | private override function validateDisplay() { 130 | if (autoWidth == false) { 131 | sprite.maxWidth = _width != 0 ? _width : _textWidth; 132 | }else if (sprite.textAlign == h2d.Text.Align.Right){ 133 | sprite.x =_width; 134 | }else if (sprite.textAlign == h2d.Text.Align.Center) { 135 | sprite.x = (_left) + (_width / 2); 136 | } 137 | } 138 | 139 | private var autoWidth(get, null):Bool; 140 | private function get_autoWidth():Bool { 141 | return parentComponent.autoWidth; 142 | } 143 | 144 | private override function measureText() { 145 | _textWidth = sprite.textWidth; 146 | _textHeight = sprite.textHeight; 147 | 148 | _textWidth = Math.round(_textWidth); 149 | _textHeight = Math.round(_textHeight); 150 | 151 | if (_textWidth % 2 != 0) { 152 | _textWidth++; 153 | } 154 | if (_textHeight % 2 == 0) { 155 | _textHeight++; 156 | } 157 | } 158 | 159 | private function createText():h2d.Text { 160 | var text = new h2d.Text(hxd.res.DefaultFont.get(), parentComponent); 161 | text.lineBreak = false; 162 | return text; 163 | } 164 | 165 | private function getAlign(align:String):h2d.Text.Align { 166 | return switch(align) { 167 | case "left": h2d.Text.Align.Left; 168 | case "right": h2d.Text.Align.Right; 169 | case "center": h2d.Text.Align.Center; 170 | case _: h2d.Text.Align.Left; //TODO - justify 171 | } 172 | } 173 | 174 | private function normalizeText(text:String):String { 175 | if (text == null) { 176 | return ""; 177 | } 178 | text = StringTools.replace(text, "\\n", "\n"); 179 | return text; 180 | } 181 | } -------------------------------------------------------------------------------- /haxe/ui/backend/TextInputImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import h2d.TextInput; 4 | import haxe.ui.core.InteractiveComponent; 5 | import haxe.ui.events.FocusEvent; 6 | import haxe.ui.events.KeyboardEvent; 7 | import haxe.ui.events.UIEvent; 8 | import hxd.Event; 9 | import hxd.Key; 10 | 11 | class TextInputImpl extends TextDisplayImpl { 12 | 13 | var textInput: TextInput; 14 | 15 | public function new() { 16 | super(); 17 | } 18 | 19 | private override function createText() { 20 | textInput = new TextInput(hxd.res.DefaultFont.get()); 21 | textInput.lineBreak = false; 22 | textInput.onChange = onChange; 23 | textInput.onClick = function(e) { 24 | cast(parentComponent, InteractiveComponent).focus = true; 25 | } 26 | return textInput; 27 | } 28 | 29 | // we're actually going to override this function so that it always returns 30 | // h2d.Text.Align.Left - this is because heaps text input doesnt seem to like 31 | // center aligned text (or right aligned), for now will simply turn it off 32 | /*private override function getAlign(align:String):h2d.Text.Align { 33 | return h2d.Text.Align.Left; 34 | }*/ 35 | 36 | public override function focus() { 37 | Toolkit.callLater(function() { 38 | textInput.focus(); 39 | }); 40 | } 41 | 42 | public override function blur() { 43 | @:privateAccess textInput.interactive.blur(); 44 | } 45 | 46 | private function onChange() { 47 | _text = textInput.text; 48 | _htmlText = textInput.text; 49 | 50 | measureText(); 51 | 52 | if (_inputData.onChangedCallback != null) { 53 | _inputData.onChangedCallback(); 54 | } 55 | 56 | if (parentComponent != null) { 57 | parentComponent.dispatch(new UIEvent(UIEvent.CHANGE)); 58 | } 59 | } 60 | 61 | private override function validateDisplay() { 62 | super.validateDisplay(); 63 | 64 | textInput.inputWidth = Math.round(textInput.maxWidth); // clip text input display to text component's width 65 | } 66 | 67 | private override function resizeFont(fontSizeValue:Int, isBitmap:Bool) { 68 | var temp = sprite.font.clone(); 69 | if (isBitmap) { 70 | temp.resizeTo(-fontSizeValue); 71 | } else { 72 | if (temp == hxd.res.DefaultFont.get()) { 73 | temp = hxd.res.DefaultFont.get().clone(); 74 | } 75 | temp.resizeTo(fontSizeValue); 76 | } 77 | sprite.font = temp; 78 | temp = null; 79 | } 80 | 81 | private override function validateStyle():Bool { 82 | var measureTextRequired:Bool = super.validateStyle(); 83 | 84 | if ( _inputData.password) { 85 | trace("TextInput password mode isn't supported in Heaps."); 86 | _inputData.password = false; 87 | } 88 | 89 | if (parentComponent.disabled) { 90 | textInput.canEdit = false; 91 | } else { 92 | textInput.canEdit = true; 93 | } 94 | 95 | return measureTextRequired; 96 | } 97 | 98 | private var _onKeyDown:KeyboardEvent->Void = null; 99 | public var onKeyDown(null, set):KeyboardEvent->Void; 100 | private function set_onKeyDown(value:KeyboardEvent->Void):KeyboardEvent->Void { 101 | _onKeyDown = value; 102 | if (_onKeyDown == null && _onKeyUp == null && _onKeyPress == null) { 103 | unregisterInernalEvents(); 104 | return value; 105 | } 106 | registerInternalEvents(); 107 | return value; 108 | } 109 | 110 | private var _onKeyUp:KeyboardEvent->Void = null; 111 | public var onKeyUp(null, set):KeyboardEvent->Void; 112 | private function set_onKeyUp(value:KeyboardEvent->Void):KeyboardEvent->Void { 113 | _onKeyUp = value; 114 | if (_onKeyDown == null && _onKeyUp == null && _onKeyPress == null) { 115 | unregisterInernalEvents(); 116 | return value; 117 | } 118 | registerInternalEvents(); 119 | return value; 120 | } 121 | 122 | private var _onKeyPress:KeyboardEvent->Void = null; 123 | public var onKeyPress(null, set):KeyboardEvent->Void; 124 | private function set_onKeyPress(value:KeyboardEvent->Void):KeyboardEvent->Void { 125 | _onKeyPress = value; 126 | if (_onKeyDown == null && _onKeyUp == null && _onKeyPress == null) { 127 | unregisterInernalEvents(); 128 | return value; 129 | } 130 | registerInternalEvents(); 131 | return value; 132 | } 133 | 134 | private var _internalEventsRegistered = false; 135 | private function registerInternalEvents() { 136 | if (_internalEventsRegistered) { 137 | return; 138 | } 139 | _internalEventsRegistered = true; 140 | textInput.onKeyDown = onKeyDownInternal; 141 | textInput.onKeyUp = onKeyUpInternal; 142 | } 143 | 144 | // heaps doesnt have a keypress event, so we'll hold onto down keys in order to dispatch the press event 145 | private var _downKeys:Map = new Map(); 146 | private function unregisterInernalEvents() { 147 | textInput.onKeyDown = null; 148 | textInput.onKeyUp = null; 149 | _internalEventsRegistered = false; 150 | } 151 | 152 | private function onKeyDownInternal(e:Event) { 153 | _downKeys.set(e.keyCode, true); 154 | dispatchEvent(KeyboardEvent.KEY_DOWN, e.keyCode); 155 | } 156 | 157 | private function onKeyUpInternal(e:Event) { 158 | var hadDownKey = (_downKeys.exists(e.keyCode) && _downKeys.get(e.keyCode) == true); 159 | _downKeys.remove(e.keyCode); 160 | dispatchEvent(KeyboardEvent.KEY_UP, e.keyCode); 161 | if (hadDownKey) { 162 | dispatchEvent(KeyboardEvent.KEY_PRESS, e.keyCode); 163 | } 164 | } 165 | 166 | private function dispatchEvent(type:String, keyCode:Int) { 167 | var event = new KeyboardEvent(type); 168 | event.keyCode = keyCode; 169 | event.altKey = Key.isDown(Key.ALT); 170 | event.shiftKey = Key.isDown(Key.SHIFT); 171 | event.ctrlKey = Key.isDown(Key.CTRL); 172 | switch (type) { 173 | case KeyboardEvent.KEY_DOWN: 174 | if (_onKeyDown != null) { 175 | _onKeyDown(event); 176 | } 177 | case KeyboardEvent.KEY_UP: 178 | if (_onKeyUp != null) { 179 | _onKeyUp(event); 180 | } 181 | case KeyboardEvent.KEY_PRESS: 182 | if (_onKeyPress != null) { 183 | _onKeyPress(event); 184 | } 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /haxe/ui/backend/TimerImpl.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | class TimerImpl { 4 | static private var __timers:Array = []; 5 | 6 | static public function update() { 7 | var currentTime:Float = hxd.Timer.lastTimeStamp; 8 | var count:Int = __timers.length; 9 | for (i in 0...count) { 10 | var timer:TimerImpl = __timers[i]; 11 | if (!timer._stopped && currentTime >= timer._end) { 12 | timer._end = currentTime + timer._delay; 13 | if (timer.callback != null) { 14 | timer.callback(); 15 | } 16 | } 17 | } 18 | 19 | while(--count >= 0) { 20 | var timer:TimerImpl = __timers[count]; 21 | if (timer._stopped) { 22 | __timers.remove(timer); 23 | } 24 | } 25 | } 26 | 27 | public var callback:Void->Void = null; 28 | private var _end:Float; 29 | private var _delay:Float; 30 | private var _stopped:Bool; 31 | 32 | public function new(delay:Int, callback:Void->Void) { 33 | this.callback = callback; 34 | _delay = delay / 1000; 35 | _end = hxd.Timer.lastTimeStamp + _delay; 36 | __timers.push(this); 37 | } 38 | 39 | public function stop() { 40 | callback = null; 41 | _stopped = true; 42 | } 43 | } -------------------------------------------------------------------------------- /haxe/ui/backend/ToolkitOptions.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend; 2 | 3 | import h2d.Object; 4 | import hxd.App; 5 | 6 | typedef ToolkitOptions = { 7 | @:optional var root:Object; 8 | @:optional var manualUpdate:Bool; 9 | } 10 | -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/EventMapper.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend.heaps; 2 | 3 | class EventMapper { 4 | public static var HAXEUI_TO_HEAPS:Map = [ 5 | haxe.ui.events.MouseEvent.MOUSE_MOVE => EventType.MOUSE_MOVE, 6 | haxe.ui.events.MouseEvent.MOUSE_OVER => EventType.MOUSE_OVER, 7 | haxe.ui.events.MouseEvent.MOUSE_OUT => EventType.MOUSE_OUT, 8 | haxe.ui.events.MouseEvent.MOUSE_DOWN => EventType.MOUSE_DOWN, 9 | haxe.ui.events.MouseEvent.MOUSE_UP => EventType.MOUSE_UP, 10 | haxe.ui.events.MouseEvent.MOUSE_WHEEL => EventType.MOUSE_WHEEL, 11 | haxe.ui.events.MouseEvent.CLICK => EventType.CLICK, 12 | 13 | haxe.ui.events.KeyboardEvent.KEY_DOWN => EventType.KEY_DOWN, 14 | haxe.ui.events.KeyboardEvent.KEY_UP => EventType.KEY_UP 15 | ]; 16 | 17 | /*public static var HEAPS_TO_HAXEUI:Map = [ 18 | EventType.MOUSE_MOVE => haxe.ui.events.MouseEvent.MOUSE_MOVE, 19 | EventType.MOUSE_OVER => haxe.ui.events.MouseEvent.MOUSE_OVER, 20 | EventType.MOUSE_OUT => haxe.ui.events.MouseEvent.MOUSE_OUT, 21 | EventType.MOUSE_DOWN => haxe.ui.events.MouseEvent.MOUSE_DOWN, 22 | EventType.MOUSE_UP => haxe.ui.events.MouseEvent.MOUSE_UP, 23 | EventType.MOUSE_WHEEL => haxe.ui.events.MouseEvent.MOUSE_WHEEL, 24 | EventType.CLICK => haxe.ui.events.MouseEvent.CLICK, 25 | 26 | EventType.KEY_DOWN => haxe.ui.events.KeyboardEvent.KEY_DOWN, 27 | EventType.KEY_UP => haxe.ui.events.KeyboardEvent.KEY_UP 28 | ];*/ 29 | } -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/EventType.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend.heaps; 2 | 3 | enum abstract EventType(String) from String to String { 4 | var MOUSE_MOVE = "onMove"; 5 | var MOUSE_OVER = "onOver"; 6 | var MOUSE_OUT = "onOut"; 7 | var MOUSE_DOWN = "onPush"; 8 | var MOUSE_UP = "onRelease"; 9 | var MOUSE_WHEEL = "onWheel"; 10 | var CLICK = "onClick"; 11 | 12 | var KEY_DOWN = "onKeyDown"; 13 | var KEY_UP = "onKeyUp"; 14 | } -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/FilterConverter.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend.heaps; 2 | 3 | import h2d.RenderContext; 4 | import h2d.filter.Blur; 5 | import h2d.filter.ColorMatrix; 6 | import h2d.filter.Filter; 7 | import h2d.filter.Glow; 8 | import h3d.Matrix; 9 | import h3d.mat.Pass; 10 | import haxe.ui.filters.Saturate; 11 | import haxe.ui.filters.Tint; 12 | import haxe.ui.util.Color; 13 | 14 | class FilterConverter { 15 | public static function convertFilter(input:haxe.ui.filters.Filter):Filter { 16 | if (input == null) { 17 | return null; 18 | } 19 | 20 | var output:Filter = null; 21 | 22 | if ((input is haxe.ui.filters.DropShadow)) { 23 | var inputDropShadow:haxe.ui.filters.DropShadow = cast(input, haxe.ui.filters.DropShadow); 24 | if (inputDropShadow.inner) { // TODO: temp 25 | return new h2d.filter.InnerGlow(inputDropShadow.color, .1, 1, 1); 26 | } 27 | var dropShadow = new h2d.filter.DropShadow(inputDropShadow.distance, 0.785, inputDropShadow.color, inputDropShadow.alpha * 2, 1, 1, 1, true); 28 | output = dropShadow; 29 | } else if ((input is haxe.ui.filters.BoxShadow)) { 30 | var inputDropShadow:haxe.ui.filters.BoxShadow = cast(input, haxe.ui.filters.BoxShadow); 31 | if (inputDropShadow.inset) { // TODO: temp 32 | return new h2d.filter.InnerGlow(inputDropShadow.color, .1, 1, 1); 33 | } 34 | var dropShadow = new BoxShadow(inputDropShadow.offsetX,inputDropShadow.offsetY, inputDropShadow.color, inputDropShadow.alpha, inputDropShadow.blurRadius, inputDropShadow.spreadRadius, false); 35 | output = dropShadow; 36 | } else if ((input is haxe.ui.filters.Tint)) { 37 | var inputTintFilter:haxe.ui.filters.Tint = cast(input, Tint); 38 | var cM = new ColorMatrix(); 39 | var tintFilter = new TintFilter(inputTintFilter.color, inputTintFilter.amount); 40 | output = tintFilter; 41 | } else if ((input is haxe.ui.filters.Blur)) { 42 | var inputBlur:haxe.ui.filters.Blur = cast(input, haxe.ui.filters.Blur); 43 | output = new Blur(inputBlur.amount); // BlurFilter(inputBlur.amount, inputBlur.amount); 44 | } else if ((input is haxe.ui.filters.Grayscale)) { 45 | var inputGrayscale:haxe.ui.filters.Grayscale = cast(input, haxe.ui.filters.Grayscale); 46 | output = new GrayscaleFilter(inputGrayscale.amount / 100); 47 | } else if ((input is haxe.ui.filters.HueRotate)) { 48 | var inputHue:haxe.ui.filters.HueRotate = cast(input, haxe.ui.filters.HueRotate); 49 | var cM = new ColorMatrix(); 50 | cM.matrix.colorHue(inputHue.angleDegree); 51 | output = cM; 52 | } else if ((input is haxe.ui.filters.Contrast)) { 53 | var contrast:haxe.ui.filters.Contrast = cast(input, haxe.ui.filters.Contrast); 54 | var cM = new ColorMatrix(); 55 | cM.matrix.colorContrast(contrast.multiplier); 56 | output = cM; 57 | } else if ((input is haxe.ui.filters.Saturate)) { 58 | var saturate:haxe.ui.filters.Saturate = cast(input, Saturate); 59 | var cM = new ColorMatrix(); 60 | cM.matrix.colorSaturate(saturate.multiplier); 61 | output = cM; 62 | } else if ((input is haxe.ui.filters.Invert)) { 63 | var inputInvert:haxe.ui.filters.Invert = cast(input, haxe.ui.filters.Invert); 64 | output = new InvertFilter(inputInvert.multiplier); 65 | } else if ((input is haxe.ui.filters.Brightness)) { 66 | var inputBrightness:haxe.ui.filters.Brightness = cast(input, haxe.ui.filters.Brightness); 67 | output = new BrightnessFilter(inputBrightness.multiplier); 68 | } 69 | 70 | return output; 71 | } 72 | } 73 | 74 | class TintFilter extends ColorMatrix { 75 | 76 | //public var filter:ColorMatrix; 77 | 78 | // These numbers come from the CIE XYZ Color Model 79 | public static inline var LUMA_R = 0.212671; 80 | public static inline var LUMA_G = 0.71516; 81 | public static inline var LUMA_B = 0.072169; 82 | 83 | public function new(color:Int = 0, amount:Float = 1) { 84 | 85 | var color:Color = cast color; 86 | 87 | var r:Float = color.r / 255; 88 | var g:Float = color.g / 255; 89 | var b:Float = color.b / 255; 90 | var q:Float = 1 - amount; 91 | 92 | var rA:Float = amount * r; 93 | var gA:Float = amount * g; 94 | var bA:Float = amount * b; 95 | 96 | var m = new Matrix(); 97 | m.zero(); 98 | 99 | m._11 = q + rA * LUMA_R; 100 | m._12 = gA * LUMA_R; 101 | m._13 = bA * LUMA_R; 102 | m._21 = rA * LUMA_G; 103 | m._22 = q + gA * LUMA_G; 104 | m._23 = bA * LUMA_G; 105 | m._31 = rA * LUMA_B; 106 | m._32 = gA * LUMA_B; 107 | m._33 = q + bA * LUMA_B; 108 | m._44 = 1; 109 | 110 | super(m); 111 | } 112 | } 113 | 114 | class GrayscaleFilter extends ColorMatrix { 115 | /** 116 | * Color multipliers recommended by the ITU to make the result appear to the 117 | * human eye to have the correct brightness. See page 3 of the article at 118 | * http://www.itu.int/rec/R-REC-BT.601-7-201103-I/en for more information. 119 | */ 120 | private static inline var RED:Float = 0.299; 121 | private static inline var GREEN:Float = 0.587; 122 | private static inline var BLUE:Float = 0.114; 123 | 124 | public function new(amount:Float = 1) { 125 | var m = new Matrix(); 126 | m.zero(); 127 | m._11 = 1 + (RED - 1) * amount; 128 | m._12 = RED * amount; 129 | m._13 = RED * amount; 130 | m._21 = GREEN * amount; 131 | m._22 = 1 + (GREEN - 1) * amount; 132 | m._23 = GREEN * amount; 133 | m._31 = BLUE * amount; 134 | m._32 = BLUE * amount; 135 | m._33 = 1 + (BLUE - 1) * amount; 136 | m._44 = 1; 137 | 138 | super(m); 139 | } 140 | } 141 | 142 | class InvertFilter extends ColorMatrix { 143 | public function new(multiplier:Float = 1) { 144 | var m = new Matrix(); 145 | m.zero(); 146 | m._11 = -1 * multiplier; 147 | m._22 = -1 * multiplier; 148 | m._33 = -1 * multiplier; 149 | m._41 = 1; 150 | m._42 = 1; 151 | m._43 = 1; 152 | m._44 = 1; 153 | super(m); 154 | } 155 | } 156 | 157 | class BrightnessFilter extends ColorMatrix { 158 | public function new(multiplier:Float = 1) { 159 | // In html, 0 is a black image, 1 has no effect, over it's a multiplier 160 | // So we adapt 161 | if (multiplier <= 1) multiplier = (multiplier -1) * 1; 162 | if (multiplier > 1) multiplier = (multiplier -1) * 110/255; 163 | 164 | var m = new Matrix(); 165 | m.identity(); 166 | m._41 = multiplier; 167 | m._42 = multiplier; 168 | m._43 = multiplier; 169 | super(m); 170 | } 171 | } 172 | 173 | class BoxShadow extends Glow { 174 | 175 | public var offsetX:Float = 0; 176 | public var offsetY:Float = 0; 177 | public var spreadRadius:Float = 0; 178 | 179 | var alphaPass = new h3d.mat.Pass(""); 180 | var sizePass = new h3d.mat.Pass(""); 181 | 182 | /** 183 | Create a new Shadow filter. 184 | @param distance The offset of the shadow in the `angle` direction. 185 | @param angle Shadow offset direction angle. 186 | @param color The color of the shadow. 187 | @param alpha Transparency value of the shadow. 188 | @param radius The shadow glow distance in pixels. 189 | @param gain The shadow color intensity. 190 | @param quality The sample count on each pixel as a tradeoff of speed/quality. 191 | @param smoothColor Produce gradient shadow when enabled, otherwise creates hard shadow without smoothing. 192 | **/ 193 | public function new( offsetX:Float = 4, offsetY:Float = 4, color : Int = 0, alpha = 1., blurRadius : Float = 1., spreadRadius : Float = 1., inset:Bool ) { 194 | super(color, alpha, blurRadius, 1, 1, true); 195 | this.offsetX = offsetX; 196 | this.offsetY = offsetY; 197 | this.spreadRadius = spreadRadius; 198 | alphaPass.addShader(new h3d.shader.UVDelta()); 199 | sizePass.addShader(new h3d.shader.UVDelta()); 200 | } 201 | 202 | override function sync(ctx, s) { 203 | super.sync(ctx, s); 204 | boundsExtend += Math.max(Math.abs(offsetX + spreadRadius), Math.abs(offsetY + spreadRadius)); 205 | } 206 | 207 | override function draw( ctx : h3d.impl.RenderContext, t : h2d.Tile ) { 208 | setParams(); 209 | 210 | var save = ctx.textures.allocTileTarget("glowSave", t); 211 | var perW = spreadRadius/t.width; 212 | var perH = spreadRadius/t.height; 213 | sizePass.getShader(h3d.shader.UVDelta).uvScale.set(1/(1+perW), 1/(1+perH)); 214 | 215 | h3d.pass.Copy.run(t.getTexture(), save, sizePass); 216 | 217 | pass.apply(ctx, save); 218 | alphaPass.getShader(h3d.shader.UVDelta).uvDelta.set(offsetX/t.width, offsetY/t.height); 219 | h3d.pass.Copy.run(t.getTexture(), save, Alpha, alphaPass); 220 | var ret = h2d.Tile.fromTexture(save); 221 | ret.dx = offsetX; 222 | ret.dy = offsetY; 223 | 224 | return ret; 225 | } 226 | } -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/KeyboardHelper.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend.heaps; 2 | 3 | import hxd.Key; 4 | import haxe.ui.events.KeyboardEvent; 5 | import hxd.Window; 6 | 7 | class KeyboardHelper { 8 | private static var _hasOnEvent:Bool = false; 9 | 10 | private static var _callbacks:MapVoid>> = new MapVoid>>(); 11 | 12 | public static function notify(event:String, callback:KeyboardEvent->Void) { 13 | switch (event) { 14 | case KeyboardEvent.KEY_DOWN: 15 | if (_hasOnEvent == false) { 16 | Window.getInstance().addEventTarget(onEvent); 17 | _hasOnEvent = true; 18 | } 19 | case KeyboardEvent.KEY_UP: 20 | if (_hasOnEvent == false) { 21 | Window.getInstance().addEventTarget(onEvent); 22 | _hasOnEvent = true; 23 | } 24 | case KeyboardEvent.KEY_PRESS: 25 | if (_hasOnEvent == false) { 26 | Window.getInstance().addEventTarget(onEvent); 27 | _hasOnEvent = true; 28 | } 29 | } 30 | 31 | var list = _callbacks.get(event); 32 | if (list == null) { 33 | list = new ArrayVoid>(); 34 | _callbacks.set(event, list); 35 | } 36 | 37 | list.push(callback); 38 | } 39 | 40 | public static function remove(event:String, callback:KeyboardEvent->Void) { 41 | var list = _callbacks.get(event); 42 | if (list != null) { 43 | list.remove(callback); 44 | if (list.length == 0) { 45 | _callbacks.remove(event); 46 | } 47 | } 48 | } 49 | 50 | private static function onEvent(e:hxd.Event) { 51 | switch (e.kind) { 52 | case EKeyDown: 53 | onKeyDown(e); 54 | case EKeyUp: 55 | onKeyUp(e); 56 | case _: 57 | } 58 | } 59 | 60 | private static var _shiftDown:Bool = false; 61 | private static var _ctrlDown:Bool = false; 62 | private static var _altDown:Bool = false; 63 | private static function onKeyDown(e:hxd.Event) { 64 | var list = _callbacks.get(KeyboardEvent.KEY_DOWN); 65 | if (list == null || list.length == 0) { 66 | return; 67 | } 68 | 69 | switch (e.keyCode) { 70 | case Key.SHIFT: 71 | _shiftDown = true; 72 | case Key.ALT: 73 | _altDown = true; 74 | case Key.CTRL: 75 | _ctrlDown = true; 76 | } 77 | 78 | list = list.copy(); 79 | var event = new KeyboardEvent(KeyboardEvent.KEY_DOWN); 80 | event.shiftKey = _shiftDown; 81 | event.ctrlKey = _ctrlDown; 82 | event.altKey = _altDown; 83 | @:privateAccess event._originalEvent = e; 84 | event.keyCode = e.keyCode; 85 | for (l in list) { 86 | l(event); 87 | } 88 | } 89 | 90 | private static function onKeyUp(e:hxd.Event) { 91 | var list = _callbacks.get(KeyboardEvent.KEY_UP); 92 | if (list == null || list.length == 0) { 93 | return; 94 | } 95 | 96 | switch (e.keyCode) { 97 | case Key.SHIFT: 98 | _shiftDown = false; 99 | case Key.ALT: 100 | _altDown = false; 101 | case Key.CTRL: 102 | _ctrlDown = false; 103 | } 104 | 105 | list = list.copy(); 106 | var event = new KeyboardEvent(KeyboardEvent.KEY_UP); 107 | event.shiftKey = _shiftDown; 108 | event.ctrlKey = _ctrlDown; 109 | event.altKey = _altDown; 110 | @:privateAccess event._originalEvent = e; 111 | event.keyCode = e.keyCode; 112 | for (l in list) { 113 | l(event); 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/MouseHelper.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend.heaps; 2 | 3 | import haxe.ui.components.TextArea; 4 | import haxe.ui.components.TextField; 5 | import haxe.ui.core.Screen; 6 | import haxe.ui.events.MouseEvent; 7 | import hxd.Window; 8 | 9 | class MouseHelper { 10 | public static var currentMouseX:Float = 0; 11 | public static var currentMouseY:Float = 0; 12 | 13 | private static var _hasOnEvent:Bool = false; 14 | 15 | private static var _callbacks:MapVoid>> = new MapVoid>>(); 16 | 17 | public static function notify(event:String, callback:MouseEvent->Void) { 18 | switch (event) { 19 | case MouseEvent.MOUSE_DOWN: 20 | if (_hasOnEvent == false) { 21 | Window.getInstance().addEventTarget(onEvent); 22 | _hasOnEvent = true; 23 | } 24 | case MouseEvent.MOUSE_UP: 25 | if (_hasOnEvent == false) { 26 | Window.getInstance().addEventTarget(onEvent); 27 | _hasOnEvent = true; 28 | } 29 | case MouseEvent.MOUSE_MOVE: 30 | if (_hasOnEvent == false) { 31 | Window.getInstance().addEventTarget(onEvent); 32 | _hasOnEvent = true; 33 | } 34 | case MouseEvent.MOUSE_WHEEL: 35 | if (_hasOnEvent == false) { 36 | Window.getInstance().addEventTarget(onEvent); 37 | _hasOnEvent = true; 38 | } 39 | } 40 | 41 | var list = _callbacks.get(event); 42 | if (list == null) { 43 | list = new ArrayVoid>(); 44 | _callbacks.set(event, list); 45 | } 46 | 47 | list.push(callback); 48 | } 49 | 50 | public static function remove(event:String, callback:MouseEvent->Void) { 51 | var list = _callbacks.get(event); 52 | if (list != null) { 53 | list.remove(callback); 54 | if (list.length == 0) { 55 | _callbacks.remove(event); 56 | } 57 | } 58 | } 59 | 60 | private static var _isCapturing:Bool = false; 61 | private static function onEvent(e:hxd.Event) { 62 | /* 63 | var scene = Screen.instance.scene; 64 | if (scene != null) { 65 | var xpos = Window.getInstance().mouseX / Toolkit.scaleX; 66 | var ypos = Window.getInstance().mouseY / Toolkit.scaleY; 67 | var b = Screen.instance.hasComponentUnderPoint(xpos, ypos); 68 | var i = scene.getInteractive(xpos, ypos); 69 | var isTextField = false; 70 | if (i != null && ((i.parent is TextField) || (i.parent is TextArea))) { 71 | isTextField = true; 72 | } 73 | var f = scene.getFocus(); 74 | if (f != null && ((f.parent.parent is TextField) || (f.parent.parent is TextArea))) { 75 | isTextField = true; 76 | } 77 | 78 | if (b == true && isTextField == false) { 79 | e.cancel = true; 80 | e.propagate = false; 81 | if (_isCapturing == false) { 82 | _isCapturing = true; 83 | hxd.System.setNativeCursor(Default); 84 | scene.startCapture(onEvent); 85 | } 86 | } else { 87 | if (_isCapturing == true) { 88 | _isCapturing = false; 89 | if (i != null) { 90 | hxd.System.setNativeCursor(i.cursor); 91 | } 92 | scene.stopCapture(); 93 | } 94 | } 95 | } 96 | */ 97 | 98 | switch (e.kind) { 99 | case EMove: 100 | onMouseMove(e); 101 | case EPush: 102 | onMouseDown(e); 103 | case ERelease | EReleaseOutside: 104 | onMouseUp(e); 105 | case EWheel: 106 | onMouseWheel(e); 107 | case _: 108 | } 109 | } 110 | 111 | private static function onMouseMove(e:hxd.Event) { 112 | currentMouseX = e.relX; 113 | currentMouseY = e.relY; 114 | var list = _callbacks.get(MouseEvent.MOUSE_MOVE); 115 | if (list == null || list.length == 0) { 116 | return; 117 | } 118 | 119 | list = list.copy(); 120 | list.reverse(); 121 | 122 | var event = new MouseEvent(MouseEvent.MOUSE_MOVE); 123 | @:privateAccess event._originalEvent = e; 124 | event.screenX = e.relX; 125 | event.screenY = e.relY; 126 | for (l in list) { 127 | l(event); 128 | if (event.canceled) { 129 | break; 130 | } 131 | } 132 | } 133 | 134 | private static function onMouseDown(e:hxd.Event) { 135 | var list = _callbacks.get(MouseEvent.MOUSE_DOWN); 136 | if (list == null || list.length == 0) { 137 | return; 138 | } 139 | 140 | list = list.copy(); 141 | list.reverse(); 142 | 143 | var event = new MouseEvent(MouseEvent.MOUSE_DOWN); 144 | @:privateAccess event._originalEvent = e; 145 | event.screenX = e.relX; 146 | event.screenY = e.relY; 147 | event.data = e.button; 148 | for (l in list) { 149 | l(event); 150 | if (event.canceled) { 151 | break; 152 | } 153 | } 154 | } 155 | 156 | private static function onMouseUp(e:hxd.Event) { 157 | var list = _callbacks.get(MouseEvent.MOUSE_UP); 158 | if (list == null || list.length == 0) { 159 | return; 160 | } 161 | 162 | list = list.copy(); 163 | list.reverse(); 164 | 165 | var event = new MouseEvent(MouseEvent.MOUSE_UP); 166 | @:privateAccess event._originalEvent = e; 167 | event.screenX = e.relX; 168 | event.screenY = e.relY; 169 | event.data = e.button; 170 | for (l in list) { 171 | l(event); 172 | if (event.canceled) { 173 | break; 174 | } 175 | } 176 | } 177 | 178 | private static function onMouseWheel(e:hxd.Event) { 179 | var list = _callbacks.get(MouseEvent.MOUSE_WHEEL); 180 | if (list == null || list.length == 0) { 181 | return; 182 | } 183 | 184 | list = list.copy(); 185 | list.reverse(); 186 | 187 | var event = new MouseEvent(MouseEvent.MOUSE_WHEEL); 188 | @:privateAccess event._originalEvent = e; 189 | event.delta = e.wheelDelta; 190 | for (l in list) { 191 | l(event); 192 | if (event.canceled) { 193 | break; 194 | } 195 | } 196 | } 197 | } -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/SDFFonts.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend.heaps; 2 | 3 | class SDFFonts { 4 | private static var _sdfFonts:Map = new Map(); 5 | public static function register(name:String, channel:h2d.Font.SDFChannel = 0, alphaCutoff:Float = 0.5, smoothing:Float = 0.5) { 6 | _sdfFonts.set(name, { 7 | channel: channel, 8 | alphaCutoff: alphaCutoff, 9 | smoothing: smoothing 10 | }); 11 | } 12 | 13 | public static function get(name:String):{channel:h2d.Font.SDFChannel, alphaCutoff:Float, smoothing:Float} { 14 | return _sdfFonts.get(name); 15 | } 16 | } -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/ScreenUtils.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend.heaps; 2 | 3 | class ScreenUtils { 4 | private static var _dpi:Float = 0; 5 | public static var dpi(get, null):Float; 6 | private static function get_dpi():Float { 7 | #if js 8 | 9 | if (_dpi != 0) { 10 | return _dpi; 11 | } 12 | 13 | var div = js.Browser.document.createElement("div"); 14 | div.style.width = "1in"; 15 | div.style.height = "1in"; 16 | div.style.position = "absolute"; 17 | div.style.top = "-99999px"; // position off-screen! 18 | div.style.left = "-99999px"; // position off-screen! 19 | js.Browser.document.body.appendChild(div); 20 | 21 | var devicePixelRatio:Null = js.Browser.window.devicePixelRatio; 22 | if (devicePixelRatio == null) { 23 | devicePixelRatio = 1; 24 | } 25 | 26 | _dpi = div.offsetWidth * devicePixelRatio; 27 | removeElement(div); 28 | return _dpi; 29 | 30 | #else 31 | 32 | return hxd.System.screenDPI; 33 | 34 | #end 35 | } 36 | 37 | private static var _isRetina:Null = null; 38 | public static function isRetinaDisplay():Bool { 39 | #if js 40 | 41 | if (_isRetina == null) { 42 | var query = "(-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)"; 43 | if (js.Browser.window.matchMedia(query).matches) { 44 | _isRetina = true; 45 | } else { 46 | _isRetina = false; 47 | } 48 | } 49 | return _isRetina; 50 | 51 | #else 52 | 53 | return false; 54 | 55 | #end 56 | } 57 | 58 | #if js 59 | private static function removeElement(el:js.html.Element) { 60 | // el.remove() - IE is crap 61 | if (el != null && el.parentElement != null) { 62 | el.parentElement.removeChild(el); 63 | } 64 | } 65 | #end 66 | } -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/StyleHelper.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend.heaps; 2 | 3 | import h2d.Graphics; 4 | import h2d.Tile; 5 | import haxe.ui.Toolkit; 6 | import haxe.ui.assets.ImageInfo; 7 | import haxe.ui.backend.heaps.TileCache; 8 | import haxe.ui.geom.Rectangle; 9 | import haxe.ui.geom.Slice9; 10 | import haxe.ui.styles.Style; 11 | import hxd.clipper.Rect; 12 | 13 | 14 | class StyleHelper { 15 | public static function apply(c:ComponentImpl, style:Style, w:Float, h:Float):Void { 16 | if (w <= 0 || h <= 0) { 17 | return; 18 | } 19 | 20 | var container = c.getChildAt(0); // first child is always the style-objects container 21 | if ( container == null ) { 22 | return; // fix crash resizing the window; container doesn't exist yet 23 | } 24 | 25 | var borderSize:Rectangle = new Rectangle(); 26 | borderSize.left = style.borderLeftSize; 27 | borderSize.top = style.borderTopSize; 28 | borderSize.right = style.borderRightSize; 29 | borderSize.bottom = style.borderBottomSize; 30 | 31 | var borderColor:Rect = new Rect(); 32 | borderColor.left = style.borderLeftColor; 33 | borderColor.top = style.borderTopColor; 34 | borderColor.right = style.borderRightColor; 35 | borderColor.bottom = style.borderBottomColor; 36 | 37 | var backgroundAlpha:Float = 1; 38 | if (style.backgroundOpacity != null) { 39 | backgroundAlpha = style.backgroundOpacity; 40 | } 41 | var borderAlpha:Float = 1; 42 | if (style.borderOpacity != null) { 43 | borderAlpha = style.borderOpacity; 44 | } 45 | 46 | var styleGraphics:Graphics = cast(container.getObjectByName("styleGraphics"), Graphics); 47 | if (styleGraphics == null) { 48 | styleGraphics = new Graphics(); 49 | styleGraphics.name = "styleGraphics"; 50 | container.addChildAt(styleGraphics, 0); 51 | } 52 | 53 | styleGraphics.clear(); 54 | 55 | var borderRadius:Float = 0; 56 | var isCircle = (style.borderRadius == w && style.borderRadius == h); 57 | if (!isCircle && style.borderRadius != null && style.borderRadius > 0) { 58 | borderRadius = style.borderRadius + 1; 59 | } 60 | 61 | if (style.backgroundColor != null && backgroundAlpha > 0) { 62 | if (style.backgroundColorEnd != null && style.backgroundColor != style.backgroundColorEnd) { 63 | var gradientType:String = "vertical"; 64 | if (style.backgroundGradientStyle != null) { 65 | gradientType = style.backgroundGradientStyle; 66 | } 67 | 68 | var gradientSize = 256; 69 | if (borderRadius == 0) { 70 | if (gradientType == "vertical" || gradientType == "horizontal") { 71 | var width = w - borderSize.right - borderSize.left; 72 | var height = h - borderSize.bottom - borderSize.top; 73 | var tile = TileCache.getGradient(gradientType, style.backgroundColor, style.backgroundColorEnd, gradientSize, Std.int(backgroundAlpha * 255)); 74 | styleGraphics.beginTileFill(borderSize.left, borderSize.top, width / gradientSize, height / gradientSize, tile); 75 | styleGraphics.drawRect(borderSize.left, borderSize.top, width, height); 76 | styleGraphics.endFill(); 77 | } 78 | } else { 79 | var width = w - borderSize.right - borderSize.left; 80 | var height = h - borderSize.bottom - borderSize.top; 81 | var tile = TileCache.getGradient(gradientType, style.backgroundColor, style.backgroundColorEnd, gradientSize, Std.int(backgroundAlpha * 255)); 82 | styleGraphics.beginTileFill(borderSize.left, borderSize.top, width / gradientSize, height / gradientSize, tile); 83 | drawRoundedBackground(styleGraphics, w, h, borderSize, borderRadius); 84 | styleGraphics.endFill(); 85 | } 86 | } else { 87 | if (borderRadius == 0) { 88 | styleGraphics.beginFill(style.backgroundColor, backgroundAlpha); 89 | styleGraphics.drawRect(borderSize.left, borderSize.top, w - borderSize.right - borderSize.left, h - borderSize.bottom - borderSize.top); 90 | styleGraphics.endFill(); 91 | } else { 92 | styleGraphics.beginFill(style.backgroundColor, backgroundAlpha); 93 | drawRoundedBackground(styleGraphics, w, h, borderSize, borderRadius); 94 | styleGraphics.endFill(); 95 | } 96 | } 97 | } 98 | 99 | if (style.backgroundImage != null) { 100 | Toolkit.assets.getImage(style.backgroundImage, function(imageInfo:ImageInfo) { 101 | var bgImageGraphics:Graphics = null; 102 | if (container.numChildren == 1) { 103 | bgImageGraphics = new Graphics(); 104 | container.addChildAt(bgImageGraphics, 1); 105 | } else { 106 | bgImageGraphics = cast(container.getChildAt(1), Graphics); 107 | } 108 | bgImageGraphics.clear(); 109 | 110 | var trc:Rectangle = new Rectangle(0, 0, imageInfo.width, imageInfo.height); 111 | if (style.backgroundImageClipTop != null 112 | && style.backgroundImageClipLeft != null 113 | && style.backgroundImageClipBottom != null 114 | && style.backgroundImageClipRight != null) { 115 | trc = new Rectangle(style.backgroundImageClipLeft, 116 | style.backgroundImageClipTop, 117 | style.backgroundImageClipRight - style.backgroundImageClipLeft, 118 | style.backgroundImageClipBottom - style.backgroundImageClipTop); 119 | } 120 | 121 | var tile = TileCache.get(style.backgroundImage); 122 | if (tile == null) { 123 | tile = h2d.Tile.fromBitmap(imageInfo.data); 124 | tile.getTexture().filter = Linear; 125 | TileCache.set(style.backgroundImage, tile); 126 | } 127 | 128 | if (trc != null) { 129 | /* 130 | var subTile = TileCache.get(style.backgroundImage, trc); 131 | if (subTile == null) { 132 | subTile = tile.sub(trc.left, trc.top, trc.width, trc.height); 133 | TileCache.set(style.backgroundImage, subTile, trc); 134 | } 135 | tile = subTile; 136 | */ 137 | } 138 | 139 | var slice:Rectangle = null; 140 | if (style.backgroundImageSliceTop != null 141 | && style.backgroundImageSliceLeft != null 142 | && style.backgroundImageSliceBottom != null 143 | && style.backgroundImageSliceRight != null) { 144 | slice = new Rectangle(style.backgroundImageSliceLeft, 145 | style.backgroundImageSliceTop, 146 | style.backgroundImageSliceRight - style.backgroundImageSliceLeft, 147 | style.backgroundImageSliceBottom - style.backgroundImageSliceTop); 148 | } 149 | 150 | if (slice != null) { 151 | var rects:Slice9Rects = Slice9.buildRects(w, h, trc.width, trc.height, slice); 152 | var srcRects:Array = rects.src; 153 | var dstRects:Array = rects.dst; 154 | 155 | if (style.backgroundImageRepeat == "repeat") { 156 | // The image is slightly scaled down to make sure there is no visible clip one the sides 157 | var scaleX = dstRects[4].width / ( srcRects[4].width * Math.ceil(dstRects[4].width / srcRects[4].width) ); 158 | var scaleY = dstRects[4].height / ( srcRects[4].height * Math.ceil(dstRects[4].height / srcRects[4].height) ); 159 | 160 | paintTile(bgImageGraphics, tile, srcRects[0], dstRects[0], style.backgroundImage); 161 | paintTileRepeat(bgImageGraphics, tile, srcRects[1], scaleX, 1, dstRects[1], style.backgroundImage); 162 | paintTile(bgImageGraphics, tile, srcRects[2], dstRects[2], style.backgroundImage); 163 | 164 | srcRects[3].bottom--; 165 | paintTileRepeat(bgImageGraphics, tile, srcRects[3], 1, scaleY, dstRects[3], style.backgroundImage); 166 | 167 | srcRects[4].bottom--; 168 | paintTileRepeat(bgImageGraphics, tile, srcRects[4], scaleX, scaleY, dstRects[4], style.backgroundImage); 169 | 170 | srcRects[5].bottom--; 171 | paintTileRepeat(bgImageGraphics, tile, srcRects[5], 1, scaleY, dstRects[5], style.backgroundImage); 172 | 173 | dstRects[6].bottom++; 174 | paintTile(bgImageGraphics, tile, srcRects[6], dstRects[6], style.backgroundImage); 175 | dstRects[7].bottom++; 176 | paintTileRepeat(bgImageGraphics, tile, srcRects[7], scaleX, 1, dstRects[7], style.backgroundImage); 177 | dstRects[8].bottom++; 178 | paintTile(bgImageGraphics, tile, srcRects[8], dstRects[8], style.backgroundImage); 179 | } 180 | else { 181 | paintTile(bgImageGraphics, tile, srcRects[0], dstRects[0], style.backgroundImage); 182 | paintTile(bgImageGraphics, tile, srcRects[1], dstRects[1], style.backgroundImage); 183 | paintTile(bgImageGraphics, tile, srcRects[2], dstRects[2], style.backgroundImage); 184 | 185 | srcRects[3].bottom--; 186 | paintTile(bgImageGraphics, tile, srcRects[3], dstRects[3], style.backgroundImage); 187 | 188 | srcRects[4].bottom--; 189 | paintTile(bgImageGraphics, tile, srcRects[4], dstRects[4], style.backgroundImage); 190 | 191 | srcRects[5].bottom--; 192 | paintTile(bgImageGraphics, tile, srcRects[5], dstRects[5], style.backgroundImage); 193 | 194 | dstRects[6].bottom++; 195 | paintTile(bgImageGraphics, tile, srcRects[6], dstRects[6], style.backgroundImage); 196 | dstRects[7].bottom++; 197 | paintTile(bgImageGraphics, tile, srcRects[7], dstRects[7], style.backgroundImage); 198 | dstRects[8].bottom++; 199 | paintTile(bgImageGraphics, tile, srcRects[8], dstRects[8], style.backgroundImage); 200 | } 201 | } else { 202 | var scaleX:Float = 1; 203 | var scaleY:Float = 1; 204 | 205 | if (style.backgroundImageRepeat == null || style.backgroundImageRepeat == "stretch") { 206 | scaleX = w / trc.width; 207 | scaleY = h / trc.height; 208 | } 209 | else { 210 | if (style.backgroundWidth != null) { 211 | scaleX = style.backgroundWidth / trc.width; 212 | } else if (style.backgroundWidthPercent != null) { 213 | scaleX = ((w / trc.width) * style.backgroundWidthPercent) / 100; 214 | } 215 | if (style.backgroundHeight != null) { 216 | scaleY = style.backgroundHeight / trc.height; 217 | } else if (style.backgroundHeightPercent != null) { 218 | scaleY = ((h / trc.height) * style.backgroundHeightPercent) / 100; 219 | } 220 | } 221 | 222 | if (style.backgroundImageRepeat == "repeat") { 223 | paintTileRepeat(bgImageGraphics, tile, trc, scaleX, scaleY, new Rectangle(0, 0, w, h), style.backgroundImage); 224 | } 225 | else { 226 | paintTile(bgImageGraphics, tile, trc, new Rectangle(0, 0, trc.width * scaleX, trc.height * scaleY), style.backgroundImage); 227 | } 228 | } 229 | }); 230 | } 231 | 232 | if (borderAlpha > 0) { 233 | if (isCircle) { 234 | styleGraphics.lineStyle(2, borderColor.left, borderAlpha); 235 | styleGraphics.drawCircle(w / 2, h / 2, w / 2, Std.int(w)); 236 | styleGraphics.endFill(); 237 | } else if (borderRadius == 0) { 238 | if (style.borderLeftSize != null && style.borderLeftSize > 0) { 239 | styleGraphics.lineStyle(); 240 | styleGraphics.beginFill(borderColor.left, borderAlpha); 241 | styleGraphics.lineTo(0, 0); 242 | styleGraphics.lineTo(borderSize.left, borderSize.top); 243 | styleGraphics.lineTo(borderSize.left, h - borderSize.bottom); 244 | styleGraphics.lineTo(0, h); 245 | styleGraphics.endFill(); 246 | } 247 | 248 | if (style.borderRightSize != null && style.borderRightSize > 0) { 249 | styleGraphics.lineStyle(); 250 | styleGraphics.beginFill(borderColor.right, borderAlpha); 251 | styleGraphics.lineTo(w, 0); 252 | styleGraphics.lineTo(w, h); 253 | styleGraphics.lineTo(w - borderSize.right, h - borderSize.bottom); 254 | styleGraphics.lineTo(w - borderSize.right, borderSize.top); 255 | styleGraphics.endFill(); 256 | } 257 | 258 | if (style.borderTopSize != null && style.borderTopSize > 0) { 259 | styleGraphics.lineStyle(); 260 | styleGraphics.beginFill(borderColor.top, borderAlpha); 261 | styleGraphics.lineTo(0, 0); 262 | styleGraphics.lineTo(w, 0); 263 | styleGraphics.lineTo(w - borderSize.right, borderSize.top); 264 | styleGraphics.lineTo(borderSize.left, borderSize.top); 265 | styleGraphics.endFill(); 266 | } 267 | 268 | if (style.borderBottomSize != null && style.borderBottomSize > 0) { 269 | styleGraphics.lineStyle(); 270 | styleGraphics.beginFill(borderColor.bottom, borderAlpha); 271 | styleGraphics.lineTo(0, h); 272 | styleGraphics.lineTo(borderSize.left, h - borderSize.bottom); 273 | styleGraphics.lineTo(w - borderSize.right, h - borderSize.bottom); 274 | styleGraphics.lineTo(w, h); 275 | styleGraphics.endFill(); 276 | } 277 | } else { // Border radius != 0 278 | // Left 279 | if (borderSize.left != 0) { 280 | // Left-Top corner 281 | if (borderSize.left == borderSize.top) { 282 | if (borderRadius <= borderSize.left) { 283 | // Left 284 | styleGraphics.beginFill(borderColor.left, borderAlpha); 285 | styleGraphics.drawPie(borderRadius, borderRadius, borderRadius, Math.PI, Math.PI * 0.25, 10); 286 | styleGraphics.lineTo(0, borderRadius); 287 | styleGraphics.lineTo(borderRadius, borderRadius); 288 | styleGraphics.lineTo(borderSize.left, borderSize.top); 289 | styleGraphics.lineTo(borderSize.left, h * 0.5); 290 | styleGraphics.lineTo(0, h * 0.5); 291 | styleGraphics.endFill(); 292 | // Top 293 | styleGraphics.beginFill(borderColor.top, borderAlpha); 294 | styleGraphics.drawPie(borderRadius, borderRadius, borderRadius, Math.PI * 1.25, Math.PI * 0.25, 10); 295 | styleGraphics.lineTo(borderRadius, 0); 296 | styleGraphics.lineTo(w * 0.5, 0); 297 | styleGraphics.lineTo(w * 0.5, borderSize.top); 298 | styleGraphics.lineTo(borderSize.left, borderSize.top); 299 | styleGraphics.lineTo(borderRadius, borderRadius); 300 | styleGraphics.endFill(); 301 | } else { 302 | var innerRadius = borderRadius - borderSize.left; 303 | // Left 304 | styleGraphics.beginFill(borderColor.left, borderAlpha); 305 | styleGraphics.drawPieInner(borderRadius, borderRadius, borderRadius, innerRadius, Math.PI, Math.PI * 0.25, 10); 306 | styleGraphics.lineTo(0, borderRadius); 307 | styleGraphics.lineTo(borderSize.left, borderRadius); 308 | styleGraphics.lineTo(borderSize.left, h * 0.5); 309 | styleGraphics.lineTo(0, h * 0.5); 310 | styleGraphics.endFill(); 311 | // Top 312 | styleGraphics.beginFill(borderColor.top, borderAlpha); 313 | styleGraphics.drawPieInner(borderRadius, borderRadius, borderRadius, innerRadius, Math.PI * 1.25, Math.PI * 0.25, 10); 314 | styleGraphics.lineTo(borderRadius, 0); 315 | styleGraphics.lineTo(w * 0.5, 0); 316 | styleGraphics.lineTo(w * 0.5, borderSize.top); 317 | styleGraphics.lineTo(borderRadius, borderSize.top); 318 | styleGraphics.endFill(); 319 | } 320 | } else { 321 | styleGraphics.beginFill(borderColor.left, borderAlpha); 322 | if (borderRadius <= borderSize.left || borderRadius <= borderSize.top) { 323 | drawUnevenBordersCurve(styleGraphics, borderRadius, borderRadius, borderRadius, 324 | Math.PI, Math.PI * 0.5 * borderSize.left / (borderSize.left + borderSize.top)); 325 | styleGraphics.lineTo(borderSize.left, borderSize.top); 326 | styleGraphics.lineTo(borderSize.left, h * 0.5); 327 | styleGraphics.lineTo(0, h * 0.5); 328 | } else { 329 | drawUnevenBordersCorner(styleGraphics, borderRadius, borderRadius, borderRadius, borderRadius - borderSize.left, borderRadius - borderSize.top, 330 | Math.PI, Math.PI * 0.5 * borderSize.left / (borderSize.left + borderSize.top)); 331 | styleGraphics.moveTo(0, borderRadius); 332 | styleGraphics.lineTo(borderSize.left, borderRadius); 333 | styleGraphics.lineTo(borderSize.left, h * 0.5); 334 | styleGraphics.lineTo(0, h * 0.5); 335 | } 336 | styleGraphics.endFill(); 337 | } 338 | // Left-Bottom corner 339 | if (borderSize.left == borderSize.bottom) { 340 | if (borderRadius <= borderSize.left) { 341 | // Left 342 | styleGraphics.beginFill(borderColor.left, borderAlpha); 343 | styleGraphics.drawPie(borderRadius, h - borderRadius, borderRadius, Math.PI * 0.75, Math.PI * 0.25, 10); 344 | styleGraphics.lineTo(0, h * 0.5); 345 | styleGraphics.lineTo(borderSize.left, h * 0.5); 346 | styleGraphics.lineTo(borderSize.left, h - borderSize.bottom); 347 | styleGraphics.lineTo(borderRadius, h - borderRadius); 348 | styleGraphics.lineTo(0, h - borderRadius); 349 | styleGraphics.endFill(); 350 | // Bottom 351 | styleGraphics.beginFill(borderColor.bottom, borderAlpha); 352 | styleGraphics.drawPie(borderRadius, h - borderRadius, borderRadius, Math.PI * 0.5, Math.PI * 0.25, 10); 353 | styleGraphics.lineTo(w * 0.5, h); 354 | styleGraphics.lineTo(w * 0.5, h - borderSize.bottom); 355 | styleGraphics.lineTo(borderSize.left, h - borderSize.bottom); 356 | styleGraphics.lineTo(borderRadius, h - borderRadius); 357 | styleGraphics.lineTo(borderRadius, h); 358 | styleGraphics.endFill(); 359 | } else { 360 | var innerRadius = borderRadius - borderSize.left; 361 | // Left 362 | styleGraphics.beginFill(borderColor.left, borderAlpha); 363 | styleGraphics.drawPieInner(borderRadius, h - borderRadius, borderRadius, innerRadius, Math.PI * 0.75, Math.PI * 0.25, 10); 364 | styleGraphics.lineTo(0, h * 0.5); 365 | styleGraphics.lineTo(borderSize.left, h * 0.5); 366 | styleGraphics.lineTo(borderSize.left, h - borderRadius); 367 | styleGraphics.lineTo(0, h - borderRadius); 368 | styleGraphics.endFill(); 369 | // Bottom 370 | styleGraphics.beginFill(borderColor.bottom, borderAlpha); 371 | styleGraphics.drawPieInner(borderRadius, h - borderRadius, borderRadius, innerRadius, Math.PI * 0.5, Math.PI * 0.25, 10); 372 | styleGraphics.lineTo(w * 0.5, h); 373 | styleGraphics.lineTo(w * 0.5, h - borderSize.bottom); 374 | styleGraphics.lineTo(borderRadius, h - borderSize.bottom); 375 | styleGraphics.lineTo(borderRadius, h); 376 | styleGraphics.endFill(); 377 | } 378 | } else { 379 | styleGraphics.beginFill(borderColor.left, borderAlpha); 380 | if (borderRadius <= borderSize.left || borderRadius <= borderSize.bottom) { 381 | drawUnevenBordersCurve(styleGraphics, borderRadius, h - borderRadius, borderRadius, 382 | Math.PI * (0.5 + 0.5 * borderSize.bottom / (borderSize.left + borderSize.bottom)), 383 | Math.PI * 0.5 * borderSize.left / (borderSize.left + borderSize.bottom)); 384 | styleGraphics.lineTo(0, h * 0.5); 385 | styleGraphics.lineTo(borderSize.left, h * 0.5); 386 | styleGraphics.lineTo(borderSize.left, h - borderSize.bottom); 387 | } else { 388 | drawUnevenBordersCorner(styleGraphics, borderRadius, h - borderRadius, borderRadius, borderRadius - borderSize.left, borderRadius - borderSize.bottom, 389 | Math.PI * (0.5 + 0.5 * borderSize.bottom / (borderSize.left + borderSize.bottom)), 390 | Math.PI * 0.5 * borderSize.left / (borderSize.left + borderSize.bottom)); 391 | styleGraphics.moveTo(0, h * 0.5); 392 | styleGraphics.lineTo(borderSize.left, h * 0.5); 393 | styleGraphics.lineTo(borderSize.left, h - borderRadius); 394 | styleGraphics.lineTo(0, h - borderRadius); 395 | } 396 | styleGraphics.endFill(); 397 | } 398 | } 399 | 400 | // Top 401 | if(borderSize.top != 0) { 402 | // Left-Top corner 403 | if (borderSize.left != borderSize.top) { 404 | styleGraphics.beginFill(borderColor.top, borderAlpha); 405 | if (borderRadius <= borderSize.left || borderRadius <= borderSize.top) { 406 | drawUnevenBordersCurve(styleGraphics, borderRadius, borderRadius, borderRadius, 407 | Math.PI * (1 + 0.5 * borderSize.left / (borderSize.left + borderSize.top)), 408 | Math.PI * 0.5 * borderSize.top / (borderSize.left + borderSize.top)); 409 | styleGraphics.lineTo(w * 0.5, 0); 410 | styleGraphics.lineTo(w * 0.5, borderSize.top); 411 | styleGraphics.lineTo(borderSize.left, borderSize.top); 412 | } else { 413 | drawUnevenBordersCorner(styleGraphics, borderRadius, borderRadius, borderRadius, borderRadius - borderSize.left, borderRadius - borderSize.top, 414 | Math.PI * (1 + 0.5 * borderSize.left / (borderSize.left + borderSize.top)), 415 | Math.PI * 0.5 * borderSize.top / (borderSize.left + borderSize.top)); 416 | styleGraphics.moveTo(borderRadius, 0); 417 | styleGraphics.lineTo(w * 0.5, 0); 418 | styleGraphics.lineTo(w * 0.5, borderSize.top); 419 | styleGraphics.lineTo(borderRadius, borderSize.top); 420 | } 421 | styleGraphics.endFill(); 422 | } 423 | // Right-Top corner 424 | if (borderSize.right != borderSize.top) { 425 | styleGraphics.beginFill(borderColor.top, borderAlpha); 426 | if (borderRadius <= borderSize.right || borderRadius <= borderSize.top) { 427 | drawUnevenBordersCurve(styleGraphics, w - borderRadius, borderRadius, borderRadius, 428 | Math.PI * -0.5, Math.PI * 0.5 * borderSize.top / (borderSize.right + borderSize.top)); 429 | styleGraphics.lineTo(w - borderSize.right, borderSize.top); 430 | styleGraphics.lineTo(w * 0.5, borderSize.top); 431 | styleGraphics.lineTo(w * 0.5, 0); 432 | } else { 433 | drawUnevenBordersCorner(styleGraphics, w - borderRadius, borderRadius, borderRadius, borderRadius - borderSize.right, borderRadius - borderSize.top, 434 | Math.PI * -0.5, Math.PI * 0.5 * borderSize.top / (borderSize.right + borderSize.top)); 435 | styleGraphics.moveTo(w - borderRadius, 0); 436 | styleGraphics.lineTo(w * 0.5, 0); 437 | styleGraphics.lineTo(w * 0.5, borderSize.top); 438 | styleGraphics.lineTo(w - borderRadius, borderSize.top); 439 | } 440 | styleGraphics.endFill(); 441 | } 442 | } 443 | 444 | // Right 445 | if(borderSize.right != 0) { 446 | // Right-Top corner 447 | if (borderSize.right == borderSize.top) { 448 | if (borderRadius <= borderSize.right) { 449 | // Right 450 | styleGraphics.beginFill(borderColor.right, borderAlpha); 451 | styleGraphics.drawPie(w - borderRadius, borderRadius, borderRadius, Math.PI * -0.25, Math.PI * 0.25, 10); 452 | styleGraphics.lineTo(w, borderRadius); 453 | styleGraphics.lineTo(w - borderRadius, borderRadius); 454 | styleGraphics.lineTo(w - borderSize.right, borderSize.top); 455 | styleGraphics.lineTo(w - borderSize.right, h * 0.5); 456 | styleGraphics.lineTo(w, h * 0.5); 457 | styleGraphics.endFill(); 458 | // Top 459 | styleGraphics.beginFill(borderColor.top, borderAlpha); 460 | styleGraphics.drawPie(w - borderRadius, borderRadius, borderRadius, Math.PI * -0.5, Math.PI * 0.25, 10); 461 | styleGraphics.lineTo(w - borderRadius, 0); 462 | styleGraphics.lineTo(w * 0.5, 0); 463 | styleGraphics.lineTo(w * 0.5, borderSize.top); 464 | styleGraphics.lineTo(w - borderSize.right, borderSize.top); 465 | styleGraphics.lineTo(w - borderRadius, borderRadius); 466 | styleGraphics.endFill(); 467 | } else { 468 | var innerRadius = borderRadius - borderSize.right; 469 | // Right 470 | styleGraphics.beginFill(borderColor.right, borderAlpha); 471 | styleGraphics.drawPieInner(w - borderRadius, borderRadius, borderRadius, innerRadius, Math.PI * -0.25, Math.PI * 0.25, 10); 472 | styleGraphics.lineTo(w, borderRadius); 473 | styleGraphics.lineTo(w - borderSize.right, borderRadius); 474 | styleGraphics.lineTo(w - borderSize.right, h * 0.5); 475 | styleGraphics.lineTo(w, h * 0.5); 476 | styleGraphics.endFill(); 477 | // Top 478 | styleGraphics.beginFill(borderColor.top, borderAlpha); 479 | styleGraphics.drawPieInner(w - borderRadius, borderRadius, borderRadius, innerRadius, Math.PI * -0.5, Math.PI * 0.25, 10); 480 | styleGraphics.lineTo(w - borderRadius, 0); 481 | styleGraphics.lineTo(w * 0.5, 0); 482 | styleGraphics.lineTo(w * 0.5, borderSize.top); 483 | styleGraphics.lineTo(w - borderRadius, borderSize.top); 484 | styleGraphics.endFill(); 485 | } 486 | } else { 487 | styleGraphics.beginFill(borderColor.right, borderAlpha); 488 | if (borderRadius <= borderSize.right || borderRadius <= borderSize.top) { 489 | drawUnevenBordersCurve(styleGraphics, w - borderRadius, borderRadius, borderRadius, 490 | Math.PI * (-0.5 + 0.5 * borderSize.top / (borderSize.right + borderSize.top)), 491 | Math.PI * 0.5 * borderSize.right / (borderSize.right + borderSize.top)); 492 | styleGraphics.lineTo(w, h * 0.5); 493 | styleGraphics.lineTo(w - borderSize.right, h * 0.5); 494 | styleGraphics.lineTo(w - borderSize.right, borderSize.top); 495 | } else { 496 | drawUnevenBordersCorner(styleGraphics, w - borderRadius, borderRadius, borderRadius, borderRadius - borderSize.right, borderRadius - borderSize.top, 497 | Math.PI * (-0.5 + 0.5 * borderSize.top / (borderSize.right + borderSize.top)), 498 | Math.PI * 0.5 * borderSize.right / (borderSize.right + borderSize.top)); 499 | styleGraphics.moveTo(w, borderRadius); 500 | styleGraphics.lineTo(w - borderSize.right, borderRadius); 501 | styleGraphics.lineTo(w - borderSize.right, h * 0.5); 502 | styleGraphics.lineTo(w, h * 0.5); 503 | } 504 | styleGraphics.endFill(); 505 | } 506 | // Bottom-Right corner 507 | if (borderSize.right == borderSize.bottom) { 508 | if (borderRadius <= borderSize.right) { 509 | // Right 510 | styleGraphics.beginFill(borderColor.right, borderAlpha); 511 | styleGraphics.drawPie(w - borderRadius, h - borderRadius, borderRadius, 0, Math.PI * 0.25, 10); 512 | styleGraphics.lineTo(w, h * 0.5); 513 | styleGraphics.lineTo(w - borderSize.right, h * 0.5); 514 | styleGraphics.lineTo(w - borderSize.right, h - borderSize.bottom); 515 | styleGraphics.lineTo(w - borderRadius, h - borderRadius); 516 | styleGraphics.lineTo(w, h - borderRadius); 517 | styleGraphics.endFill(); 518 | // Bottom 519 | styleGraphics.beginFill(borderColor.bottom, borderAlpha); 520 | styleGraphics.drawPie(w - borderRadius, h - borderRadius, borderRadius, Math.PI * 0.25, Math.PI * 0.25, 10); 521 | styleGraphics.lineTo(w * 0.5, h); 522 | styleGraphics.lineTo(w * 0.5, h - borderSize.bottom); 523 | styleGraphics.lineTo(w - borderSize.right, h - borderSize.bottom); 524 | styleGraphics.lineTo(w - borderRadius, h - borderRadius); 525 | styleGraphics.lineTo(w - borderRadius, h); 526 | styleGraphics.endFill(); 527 | } else { 528 | var innerRadius = borderRadius - borderSize.right; 529 | // Right 530 | styleGraphics.beginFill(borderColor.right, borderAlpha); 531 | styleGraphics.drawPieInner(w - borderRadius, h - borderRadius, borderRadius, innerRadius, 0, Math.PI * 0.25, 10); 532 | styleGraphics.lineTo(w, h * 0.5); 533 | styleGraphics.lineTo(w - borderSize.right, h * 0.5); 534 | styleGraphics.lineTo(w - borderSize.right, h - borderRadius); 535 | styleGraphics.lineTo(w, h - borderRadius); 536 | styleGraphics.endFill(); 537 | // Bottom 538 | styleGraphics.beginFill(borderColor.bottom, borderAlpha); 539 | styleGraphics.drawPieInner(w - borderRadius, h - borderRadius, borderRadius, innerRadius, Math.PI * 0.25, Math.PI * 0.25, 10); 540 | styleGraphics.lineTo(w * 0.5, h); 541 | styleGraphics.lineTo(w * 0.5, h - borderSize.bottom); 542 | styleGraphics.lineTo(w - borderRadius, h - borderSize.bottom); 543 | styleGraphics.lineTo(w - borderRadius, h); 544 | styleGraphics.endFill(); 545 | } 546 | } else { 547 | styleGraphics.beginFill(borderColor.right, borderAlpha); 548 | if (borderRadius <= borderSize.right || borderRadius <= borderSize.bottom) { 549 | drawUnevenBordersCurve(styleGraphics, w - borderRadius, h - borderRadius, borderRadius, 550 | 0, Math.PI * 0.5 * borderSize.right / (borderSize.right + borderSize.bottom)); 551 | styleGraphics.lineTo(w - borderSize.right, h - borderSize.bottom); 552 | styleGraphics.lineTo(w - borderSize.right, h * 0.5); 553 | styleGraphics.lineTo(w, h * 0.5); 554 | } else { 555 | drawUnevenBordersCorner(styleGraphics, w - borderRadius, h - borderRadius, borderRadius, borderRadius - borderSize.right, borderRadius - borderSize.bottom, 556 | 0, Math.PI * 0.5 * borderSize.right / (borderSize.right + borderSize.bottom)); 557 | styleGraphics.moveTo(w, h * 0.5); 558 | styleGraphics.lineTo(w - borderSize.right, h * 0.5); 559 | styleGraphics.lineTo(w - borderSize.right, h - borderRadius); 560 | styleGraphics.lineTo(w, h - borderRadius); 561 | } 562 | styleGraphics.endFill(); 563 | } 564 | } 565 | 566 | // Bottom 567 | if(borderSize.bottom != 0) { 568 | // Left-Bottom corner 569 | if (borderSize.left != borderSize.bottom) { 570 | styleGraphics.beginFill(borderColor.bottom, borderAlpha); 571 | if (borderRadius <= borderSize.left || borderRadius <= borderSize.bottom) { 572 | drawUnevenBordersCurve(styleGraphics, borderRadius, h - borderRadius, borderRadius, 573 | Math.PI * 0.5, Math.PI * 0.5 * borderSize.bottom / (borderSize.left + borderSize.bottom)); 574 | styleGraphics.lineTo(borderSize.left, h - borderSize.bottom); 575 | styleGraphics.lineTo(w * 0.5, h - borderSize.bottom); 576 | styleGraphics.lineTo(w * 0.5, h); 577 | } else { 578 | drawUnevenBordersCorner(styleGraphics, borderRadius, h - borderRadius, borderRadius, borderRadius - borderSize.left, borderRadius - borderSize.bottom, 579 | Math.PI * 0.5, Math.PI * 0.5 * borderSize.bottom / (borderSize.left + borderSize.bottom)); 580 | styleGraphics.moveTo(w * 0.5, h); 581 | styleGraphics.lineTo(w * 0.5, h - borderSize.bottom); 582 | styleGraphics.lineTo(borderRadius, h - borderSize.bottom); 583 | styleGraphics.lineTo(borderRadius, h); 584 | } 585 | styleGraphics.endFill(); 586 | } 587 | // Right-Bottom corner 588 | if (borderSize.right != borderSize.bottom) { 589 | styleGraphics.beginFill(borderColor.bottom, borderAlpha); 590 | if (borderRadius <= borderSize.right || borderRadius <= borderSize.bottom) { 591 | drawUnevenBordersCurve(styleGraphics, w - borderRadius, h - borderRadius, borderRadius, 592 | Math.PI * 0.5 * borderSize.right / (borderSize.right + borderSize.bottom), 593 | Math.PI * 0.5 * borderSize.bottom / (borderSize.right + borderSize.bottom)); 594 | styleGraphics.lineTo(w * 0.5, h); 595 | styleGraphics.lineTo(w * 0.5, h - borderSize.bottom); 596 | styleGraphics.lineTo(w - borderSize.right, h - borderSize.bottom); 597 | } else { 598 | drawUnevenBordersCorner(styleGraphics, w - borderRadius, h - borderRadius, borderRadius, borderRadius - borderSize.right, borderRadius - borderSize.bottom, 599 | Math.PI * 0.5 * borderSize.right / (borderSize.right + borderSize.bottom), 600 | Math.PI * 0.5 * borderSize.bottom / (borderSize.right + borderSize.bottom)); 601 | styleGraphics.moveTo(w * 0.5, h); 602 | styleGraphics.lineTo(w * 0.5, h - borderSize.bottom); 603 | styleGraphics.lineTo(w - borderRadius, h - borderSize.bottom); 604 | styleGraphics.lineTo(w - borderRadius, h); 605 | } 606 | styleGraphics.endFill(); 607 | } 608 | } 609 | } 610 | } // End borders 611 | } 612 | 613 | private static function paintTile(g:Graphics, tile:Tile, src:Rectangle, dst:Rectangle, backgroundImage:String) { 614 | var scaleX = dst.width / src.width; 615 | var scaleY = dst.height / src.height; 616 | var sub = TileCache.get(backgroundImage + "_" + scaleX + "_" + scaleY, src); 617 | if (sub == null) { 618 | sub = tile.sub(src.left * scaleX, src.top * scaleY, src.width, src.height); 619 | TileCache.set(backgroundImage + "_" + scaleX + "_" + scaleY, sub, src); 620 | } 621 | g.smooth = true; 622 | g.beginTileFill(dst.left, dst.top, scaleX, scaleY, sub); 623 | g.drawRect(dst.left, dst.top, dst.width, dst.height); 624 | g.endFill(); 625 | } 626 | 627 | // Used to repeat part (src) of an image (tile) with a given scale (srcScaleX, srcScaleY) inside a target (dst) 628 | private static function paintTileRepeat(g:Graphics, tile:Tile, src:Rectangle, srcScaleX:Float, srcScaleY:Float, dst:Rectangle, backgroundImage:String) { 629 | var scaledw = srcScaleX * src.width; 630 | var scaledh = srcScaleY * src.height; 631 | var wCount = dst.width / scaledw; 632 | var hCount = dst.height / scaledh; 633 | 634 | var iwCount = Math.ceil(wCount); 635 | var ihCount = Math.ceil(hCount); 636 | 637 | var lastw = iwCount - 1; 638 | var lasth = ihCount - 1; 639 | 640 | // Full images 641 | for (iwCurr in 0...lastw) { 642 | for (ihCurr in 0...lasth) { 643 | paintTile(g, tile, src, new Rectangle(dst.left + iwCurr * scaledw, dst.top + ihCurr * scaledh, scaledw, scaledh), backgroundImage); 644 | } 645 | } 646 | 647 | var localRect = src.copy(); 648 | // Images clipped in width 649 | var clippedw = (wCount - lastw) * scaledw; 650 | localRect.width = (wCount - lastw) * src.width; 651 | for (ihCurr in 0...lasth) { 652 | paintTile(g, tile, localRect, new Rectangle(dst.left + lastw * scaledw, dst.top + ihCurr * scaledh, clippedw, scaledh), backgroundImage); 653 | } 654 | 655 | // Images clipped in height 656 | var clippedh = (hCount - lasth) * scaledh; 657 | localRect.width = src.width; 658 | localRect.height = (hCount - lasth) * src.height; 659 | for (iwCurr in 0...lastw) { 660 | paintTile(g, tile, localRect, new Rectangle(dst.left + iwCurr * scaledw, dst.top + lasth * scaledh, scaledw, clippedh), backgroundImage); 661 | } 662 | 663 | // Image clipped in both 664 | localRect.width = (wCount - lastw) * src.width; 665 | if (localRect.width > 1 && localRect.height > 1) { 666 | paintTile(g, tile, localRect, new Rectangle(dst.left + lastw * scaledw, dst.top + lasth * scaledh, clippedw, clippedh), backgroundImage); 667 | } 668 | } 669 | 670 | private static function drawUnevenBordersCorner(graphics:Graphics, cx:Float, cy:Float, radius:Float, startInnerRadius:Float, endInnerRadius:Float, angleStart:Float, angleLength:Float) { 671 | var nsegments = 10; 672 | var angleOffset = angleLength / (nsegments - 1); 673 | 674 | graphics.lineTo(cx + Math.cos(angleStart) * startInnerRadius, cy + Math.sin(angleStart) * endInnerRadius); 675 | var a; 676 | // Circle on the outside 677 | for (i in 0...nsegments) { 678 | a = i * angleOffset + angleStart; 679 | graphics.lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius); 680 | } 681 | // Ellipse on the inside 682 | graphics.lineTo(cx + Math.cos(angleStart + angleLength) * startInnerRadius, cy + Math.sin(angleStart + angleLength) * endInnerRadius); 683 | for (i in 0...nsegments) { 684 | a = (nsegments - 1 - i) * angleOffset + angleStart; 685 | graphics.lineTo(cx + Math.cos(a) * startInnerRadius, cy + Math.sin(a) * endInnerRadius); 686 | } 687 | } 688 | 689 | private static function drawUnevenBordersCurve(graphics:Graphics, cx:Float, cy:Float, radius:Float, angleStart:Float, angleLength:Float) { 690 | var nsegments = 10; 691 | var angleOffset = angleLength / (nsegments - 1); 692 | var a; 693 | for (i in 0...nsegments) { 694 | a = i * angleOffset + angleStart; 695 | graphics.lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius); 696 | } 697 | } 698 | 699 | private static function drawRoundedBackground(graphics:Graphics, w:Float, h:Float, borderSize:Rectangle, borderRadius:Float) { 700 | // Left-Top 701 | if (borderRadius <= borderSize.left || borderRadius <= borderSize.top) { 702 | graphics.lineTo(borderSize.left, borderSize.top); 703 | } else { 704 | graphics.lineTo(borderSize.left, borderRadius); 705 | drawBackgroundCorner(graphics, borderRadius, borderRadius, borderRadius - borderSize.left, borderRadius - borderSize.top, Math.PI, Math.PI * 0.5); 706 | } 707 | // Top-Right 708 | if (borderRadius <= borderSize.right || borderRadius <= borderSize.top) { 709 | graphics.lineTo(w - borderSize.right, borderSize.top); 710 | } else { 711 | graphics.lineTo(w - borderRadius, borderSize.top); 712 | drawBackgroundCorner(graphics, w - borderRadius, borderRadius, borderRadius - borderSize.right, borderRadius - borderSize.top, -Math.PI * 0.5, Math.PI * 0.5); 713 | } 714 | // Right-Bottom 715 | if (borderRadius <= borderSize.right || borderRadius <= borderSize.bottom) { 716 | graphics.lineTo(w - borderSize.right, h - borderSize.bottom); 717 | } else { 718 | graphics.lineTo(w - borderSize.right, h - borderRadius); 719 | drawBackgroundCorner(graphics, w - borderRadius, h - borderRadius, borderRadius - borderSize.right, borderRadius - borderSize.bottom, 0, Math.PI * 0.5); 720 | } 721 | // Bottom-Left 722 | if (borderRadius <= borderSize.left || borderRadius <= borderSize.bottom) { 723 | graphics.lineTo(borderSize.left, h - borderSize.bottom); 724 | } else { 725 | graphics.lineTo(borderRadius, h - borderSize.bottom); 726 | drawBackgroundCorner(graphics, borderRadius, h - borderRadius, borderRadius - borderSize.left, borderRadius - borderSize.bottom, Math.PI * 0.5, Math.PI * 0.5); 727 | } 728 | } 729 | 730 | private static inline function drawBackgroundCorner(graphics:Graphics, cx:Float, cy:Float, startRadius:Float, endRadius:Float, angleStart:Float, angleLength:Float) { 731 | var nsegments = startRadius != endRadius ? 200 : 100; 732 | var angleOffset = angleLength / (nsegments - 1); 733 | var a; 734 | for (i in 0...nsegments) { 735 | a = i * angleOffset + angleStart; 736 | graphics.lineTo(cx + Math.cos(a) * startRadius, cy + Math.sin(a) * endRadius); 737 | } 738 | } 739 | } -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/TileCache.hx: -------------------------------------------------------------------------------- 1 | package haxe.ui.backend.heaps; 2 | 3 | import h2d.Tile; 4 | import haxe.ui.geom.Rectangle; 5 | import haxe.ui.util.ColorUtil; 6 | 7 | class TileCache { 8 | private static var _cache:Map = new Map(); 9 | 10 | public static function set(resourceId:String, tile:Tile, trc:Rectangle = null):Tile { 11 | _cache.set(buildCacheKey(resourceId, trc), tile); 12 | return tile; 13 | } 14 | 15 | public static function get(resourceId:String, trc:Rectangle = null):Tile { 16 | return _cache.get(buildCacheKey(resourceId, trc)); 17 | } 18 | 19 | private static inline function buildCacheKey(resourceId:String, trc:Rectangle = null):String { 20 | var key = resourceId; 21 | if (trc != null) { 22 | key += "_" + trc.left + "_" + trc.top + "_" + trc.width + "_" + trc.height; 23 | } 24 | return key; 25 | } 26 | 27 | public static function getGradient(type:String, startCol:Int, endCol:Int, size:Int = 256, alpha:Int = 255):Tile { 28 | var key = type + "_" + startCol + "_" + endCol + "_" + size + "_" + alpha; 29 | if (_cache.exists(key)) { 30 | return _cache.get(key); 31 | } 32 | 33 | var alphaMask = alpha << 24; 34 | var arr = ColorUtil.buildColorArray(startCol, endCol, size); 35 | var tile:Tile = null; 36 | if (type == "vertical") { 37 | var gradient = new hxd.BitmapData(1, size); 38 | var y = 0; 39 | for (col in arr) { 40 | gradient.line(0, y, 1, y, alphaMask | col); 41 | y++; 42 | } 43 | tile = h2d.Tile.fromBitmap(gradient); 44 | } else if (type == "horizontal") { 45 | var gradient = new hxd.BitmapData(size, 1); 46 | var x = 0; 47 | for (col in arr) { 48 | gradient.line(x, 0, x, 1, alphaMask | col); 49 | x++; 50 | } 51 | tile = h2d.Tile.fromBitmap(gradient); 52 | } 53 | 54 | return tile; 55 | } 56 | } -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/_module/styles/dark/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxeui/haxeui-heaps/396f1a0704bcd8836637b3cc87a2691caafd0232/haxe/ui/backend/heaps/_module/styles/dark/main.css -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/_module/styles/default/main.css: -------------------------------------------------------------------------------- 1 | .property-grid .scrollview-contents { 2 | padding-right: -1px; 3 | } 4 | 5 | .text-tiny { 6 | font-size: 12px; 7 | } 8 | 9 | .text-small { 10 | font-size: 12px; 11 | } 12 | 13 | .text-normal { 14 | font-size: 12px; 15 | } 16 | 17 | .color-picker .controls-container .label { 18 | width: 75px; 19 | } 20 | -------------------------------------------------------------------------------- /haxe/ui/backend/heaps/_module/styles/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxeui/haxeui-heaps/396f1a0704bcd8836637b3cc87a2691caafd0232/haxe/ui/backend/heaps/_module/styles/main.css -------------------------------------------------------------------------------- /haxe/ui/backend/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |