diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d7bfc55..c6e10df 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-13, windows-latest] haxe-version: [4.2.5, 4.3.0, 4.3.1] target: [html5, linux, windows, mac] exclude: @@ -20,9 +20,9 @@ jobs: target: linux - os: windows-latest target: mac - - os: macos-latest + - os: macos-13 target: linux - - os: macos-latest + - os: macos-13 target: windows diff --git a/haxe/ui/backend/AppImpl.hx b/haxe/ui/backend/AppImpl.hx index 40b2d23..1d83eeb 100644 --- a/haxe/ui/backend/AppImpl.hx +++ b/haxe/ui/backend/AppImpl.hx @@ -2,6 +2,7 @@ package haxe.ui.backend; import flixel.FlxGame; import haxe.ui.backend.flixel.FlxHaxeUIAppState; +import lime.graphics.Image; import openfl.Lib; class AppImpl extends AppBase { @@ -23,4 +24,20 @@ class AppImpl extends AppBase { Lib.current.stage.addChild(new openfl.display.FPS(x, y, c)); } } + + private override function set_icon(value:String):String { + if (_icon == value) { + return value; + } + _icon = value; + + ToolkitAssets.instance.getImage(_icon, function(imageInfo) { + if (imageInfo != null) { + var iconImage = Image.fromBitmapData(imageInfo.data.parent.bitmap); + Lib.current.stage.window.setIcon(iconImage); + } + }); + + return value; + } } diff --git a/haxe/ui/backend/AssetsImpl.hx b/haxe/ui/backend/AssetsImpl.hx index 9fe7b90..ca44e40 100644 --- a/haxe/ui/backend/AssetsImpl.hx +++ b/haxe/ui/backend/AssetsImpl.hx @@ -93,12 +93,12 @@ class AssetsImpl extends AssetsBase { public override function imageInfoFromImageData(imageData:ImageData):ImageInfo { return { data: imageData, - width: imageData.parent.width, - height: imageData.parent.height + width: Std.int(imageData.frame.width), + height: Std.int(imageData.frame.height) } } private static inline function isEmbeddedFont(fontName:String):Bool { return fontName != "_sans" && fontName != "_serif" && fontName != "_typewriter"; } -} \ No newline at end of file +} diff --git a/haxe/ui/backend/CallLaterImpl.hx b/haxe/ui/backend/CallLaterImpl.hx index 9bdb662..06e694d 100644 --- a/haxe/ui/backend/CallLaterImpl.hx +++ b/haxe/ui/backend/CallLaterImpl.hx @@ -2,20 +2,32 @@ package haxe.ui.backend; import flixel.FlxG; +// we'll flip between two list references between updates meaning there is breathing space +// between calls rather that just filling a list and blocking class CallLaterImpl { + private static var added:Bool = false; - private static var fns:ArrayVoid> = []; + private static var current:ArrayVoid>; + private static var list1:ArrayVoid> = []; + private static var list2:ArrayVoid> = []; public function new(fn:Void->Void) { if (!added) { - FlxG.signals.preUpdate.add(onUpdate); added = true; + current = list1; + FlxG.signals.preUpdate.add(onUpdate); } - fns.insert(0, fn); + current.insert(0, fn); } private static function onUpdate() { - while (fns.length > 0) { - fns.pop()(); + var ref = current; + if (current == list1) { + current = list2; + } else { + current = list1; + } + while (ref.length > 0) { + ref.pop()(); } } -} \ No newline at end of file +} diff --git a/haxe/ui/backend/ComponentGraphicsImpl.hx b/haxe/ui/backend/ComponentGraphicsImpl.hx index 3a96566..681a06e 100644 --- a/haxe/ui/backend/ComponentGraphicsImpl.hx +++ b/haxe/ui/backend/ComponentGraphicsImpl.hx @@ -3,23 +3,80 @@ package haxe.ui.backend; import flixel.FlxSprite; import haxe.io.Bytes; import haxe.ui.core.Component; +import haxe.ui.loaders.image.ImageLoader; +import haxe.ui.util.Color; +import haxe.ui.util.Variant; import openfl.display.BitmapData; +import openfl.display.GraphicsPath; +import openfl.display.GraphicsPathCommand; +import openfl.display.Sprite; +import openfl.geom.Matrix; import openfl.geom.Rectangle; import openfl.utils.ByteArray; +@:allow(haxe.ui.backend.ComponentGraphicsSprite) class ComponentGraphicsImpl extends ComponentGraphicsBase { private var _hasSize:Bool = false; private var bitmapData:BitmapData = null; - private var sprite:FlxSprite; + private var sprite:ComponentGraphicsSprite; + + private var flashGfxSprite:Sprite = new Sprite(); + + private var _currentFillColor:Null = null; + private var _currentFillAlpha:Null = null; + private var _globalFillColor:Null = null; + private var _globalFillAlpha:Null = null; + + private var _globalLineThickness:Null = null; + private var _globalLineColor:Null = null; + private var _globalLineAlpha:Null = null; + + private var currentPath:GraphicsPath; + + public function new(component:Component) { + super(component); + sprite = new ComponentGraphicsSprite(this); + sprite.active = false; + sprite.visible = false; + _component.add(sprite); + } + + public override function clear() { + super.clear(); + if (_hasSize == false) { + return; + } + flashGfxSprite.graphics.clear(); + + sprite.pixels.fillRect(sprite.pixels.rect, 0x00000000); + sprite._needsDraw = true; + } + + public override function setPixel(x:Float, y:Float, color:Color) { + super.setPixel(x, y, color); + if (_hasSize == false) { + return; + } + flashGfxSprite.graphics.beginFill(color); + flashGfxSprite.graphics.drawRect(x, y, 1, 1); + flashGfxSprite.graphics.endFill(); + sprite._needsDraw = true; + } public override function setPixels(pixels:Bytes) { + super.setPixels(pixels); if (_hasSize == false) { - return super.setPixels(pixels); + return; } var w = Std.int(_component.width); var h = Std.int(_component.height); + if (bitmapData != null && (bitmapData.width != w || bitmapData.height != h)) { + bitmapData.dispose(); + bitmapData = null; + } + if (bitmapData == null) { bitmapData = new BitmapData(w, h, true, 0x00000000); } @@ -43,23 +100,215 @@ class ComponentGraphicsImpl extends ComponentGraphicsBase { var byteArray = ByteArray.fromBytes(newPixels); bitmapData.setPixels(new Rectangle(0, 0, bitmapData.width, bitmapData.height), byteArray); - if (this.sprite == null) { - sprite = new FlxSprite(0, 0); - _component.add(sprite); - } - sprite.width = w; sprite.height = h; - this.sprite.pixels = bitmapData; + sprite.pixels = bitmapData; + sprite.visible = (w > 0 && h > 0); + } + + public override function moveTo(x:Float, y:Float) { + super.moveTo(x, y); + if (_hasSize == false) { + return; + } + if (currentPath != null) { + currentPath.moveTo(x, y); + } else { + flashGfxSprite.graphics.moveTo(x, y); + sprite._needsDraw = true; + } + } + + public override function lineTo(x:Float, y:Float) { + super.lineTo(x, y); + if (_hasSize == false) { + return; + } + if (currentPath != null) { + currentPath.lineTo(x, y); + } else { + flashGfxSprite.graphics.lineTo(x, y); + sprite._needsDraw = true; + } + } + + public override function strokeStyle(color:Null, thickness:Null = 1, alpha:Null = 1) { + super.strokeStyle(color, thickness, alpha); + if (_hasSize == false) { + return; + } + if (currentPath == null) { + _globalLineThickness = thickness; + _globalLineColor = color; + _globalLineAlpha = alpha; + } + + flashGfxSprite.graphics.lineStyle(thickness, color, alpha); + } + + public override function circle(x:Float, y:Float, radius:Float) { + super.circle(x, y, radius); + if (_hasSize == false) { + return; + } + if (_currentFillColor != null) { + flashGfxSprite.graphics.beginFill(_currentFillColor, _currentFillAlpha); + } + flashGfxSprite.graphics.drawCircle(x, y, radius); + if (_currentFillColor != null) { + flashGfxSprite.graphics.endFill(); + } + sprite._needsDraw = true; + } + + public override function fillStyle(color:Null, alpha:Null = 1) { + super.fillStyle(color, alpha); + if (_hasSize == false) { + return; + } + if (currentPath == null) { + _globalFillColor = color; + _globalFillAlpha = alpha; + } + _currentFillColor = color; + _currentFillAlpha = alpha; + } + + public override function curveTo(controlX:Float, controlY:Float, anchorX:Float, anchorY:Float) { + super.curveTo(controlX, controlY, anchorX, anchorY); + if (_hasSize == false) { + return; + } + + if (currentPath != null) { + currentPath.curveTo(controlX, controlY, anchorX, anchorY); + } else { + flashGfxSprite.graphics.curveTo(controlX, controlY, anchorX, anchorY); + sprite._needsDraw = true; + } + } + + public override function cubicCurveTo(controlX1:Float, controlY1:Float, controlX2:Float, controlY2:Float, anchorX:Float, anchorY:Float) { + super.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY); + if (_hasSize == false) { + return; + } + if (currentPath != null) { + currentPath.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY); + } else { + flashGfxSprite.graphics.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY); + sprite._needsDraw = true; + } + } + + public override function rectangle(x:Float, y:Float, width:Float, height:Float) { + super.rectangle(x, y, width, height); + if (_hasSize == false) { + return; + } + if (_currentFillColor != null) { + flashGfxSprite.graphics.beginFill(_currentFillColor, _currentFillAlpha); + } + flashGfxSprite.graphics.drawRect(x, y, width, height); + if (_currentFillColor != null) { + flashGfxSprite.graphics.endFill(); + } + sprite._needsDraw = true; + } + + public override function image(resource:Variant, x:Null = null, y:Null = null, width:Null = null, height:Null = null) { + super.image(resource, x, y, width, height); + if (_hasSize == false) { + return; + } + ImageLoader.instance.load(resource, function(imageInfo) { + if (imageInfo != null) { + if (x == null) x = 0; + if (y == null) y = 0; + if (width == null) width = imageInfo.width; + if (height == null) height = imageInfo.height; + + var mat:Matrix = new Matrix(); + mat.scale(width / imageInfo.width, height / imageInfo.width); + mat.translate(x, y); + + flashGfxSprite.graphics.beginBitmapFill(imageInfo.data.parent.bitmap, mat); + flashGfxSprite.graphics.drawRect(x, y, width, height); + flashGfxSprite.graphics.endFill(); + sprite._needsDraw = true; + } else { + trace("could not load: " + resource); + } + }); + } + + public override function beginPath() { + super.beginPath(); + if (_hasSize == false) { + return; + } + currentPath = new GraphicsPath(); + } + + public override function closePath() { + super.closePath(); + if (_hasSize == false) { + return; + } + if (currentPath != null && currentPath.commands != null && currentPath.commands.length > 0) { + if (_currentFillColor != null) { + flashGfxSprite.graphics.beginFill(_currentFillColor, _currentFillAlpha); + } + if (currentPath.commands[0] != GraphicsPathCommand.MOVE_TO) { + currentPath.commands.insertAt(0, GraphicsPathCommand.MOVE_TO); + #if !flash + @:privateAccess currentPath.data.insertAt(0, flashGfxSprite.graphics.__positionX); + @:privateAccess currentPath.data.insertAt(0, flashGfxSprite.graphics.__positionY); + #end + } + flashGfxSprite.graphics.drawPath(currentPath.commands, currentPath.data); + if (_currentFillColor != null) { + flashGfxSprite.graphics.endFill(); + } + sprite._needsDraw = true; + } + currentPath = null; + _currentFillColor = _globalFillColor; + _currentFillAlpha = _globalFillAlpha; + + // it seems openfl forgets about lineStyle after drawing a shape; + flashGfxSprite.graphics.lineStyle(_globalLineThickness, _globalLineColor, _globalLineAlpha); } public override function resize(width:Null, height:Null) { if (width > 0 && height > 0) { if (_hasSize == false) { _hasSize = true; + sprite.makeGraphic(Std.int(width), Std.int(height), 0x00000000, true); + sprite.visible = true; replayDrawCommands(); } } } +} + +@:allow(haxe.ui.backend.ComponentGraphicsImpl) +class ComponentGraphicsSprite extends FlxSprite { + private var componentGraphics:ComponentGraphicsImpl; + + private var _needsDraw:Bool = false; + + public function new(componentGraphics:ComponentGraphicsImpl) { + super(); + this.componentGraphics = componentGraphics; + } + + public override function draw() { + if (pixels != null && _needsDraw) { + pixels.draw(componentGraphics.flashGfxSprite); + _needsDraw = false; + } + super.draw(); + } } \ No newline at end of file diff --git a/haxe/ui/backend/ComponentImpl.hx b/haxe/ui/backend/ComponentImpl.hx index 8ce079e..8c181e1 100644 --- a/haxe/ui/backend/ComponentImpl.hx +++ b/haxe/ui/backend/ComponentImpl.hx @@ -2,41 +2,31 @@ package haxe.ui.backend; import flixel.FlxG; import flixel.FlxSprite; +import flixel.FlxState; import flixel.math.FlxRect; import flixel.text.FlxText.FlxTextBorderStyle; import haxe.ui.Toolkit; +import haxe.ui.backend.TextInputImpl.TextInputEvent; import haxe.ui.backend.flixel.FlxStyleHelper; -import haxe.ui.backend.flixel.MouseHelper; -import haxe.ui.backend.flixel.StateHelper; import haxe.ui.core.Component; import haxe.ui.core.ImageDisplay; import haxe.ui.core.Platform; import haxe.ui.core.Screen; import haxe.ui.core.TextDisplay; import haxe.ui.core.TextInput; +import haxe.ui.events.KeyboardEvent; import haxe.ui.events.MouseEvent; import haxe.ui.events.UIEvent; import haxe.ui.filters.DropShadow; import haxe.ui.filters.Outline; import haxe.ui.geom.Rectangle; import haxe.ui.styles.Style; -import haxe.ui.util.MathUtil; -import openfl.events.Event; class ComponentImpl extends ComponentBase { private var _eventMap:MapVoid>; private var _surface:FlxSprite; - private var lastMouseX:Float = -1; - private var lastMouseY:Float = -1; - - // For doubleclick detection - private var _lastClickTime:Float = 0; - private var _lastClickTimeDiff:Float = MathUtil.MAX_INT; - private var _lastClickX:Float = -1; - private var _lastClickY:Float = -1; - private var asComponent:Component; public function new() { @@ -56,9 +46,9 @@ class ComponentImpl extends ComponentBase { scrollFactor.set(0, 0); // ui doesn't scroll by default _surface = new FlxSprite(); - _surface.makeGraphic(1, 1, 0x0, true); _surface.pixelPerfectRender = true; - _surface.moves = false; + _surface.active = false; + _surface.visible = false; add(_surface); //recursiveReady(); @@ -71,6 +61,17 @@ class ComponentImpl extends ComponentBase { } } + private var _state:FlxState; + public var state(get, set):FlxState; + private function get_state():FlxState { + return findRootComponent()._state; + } + private function set_state(value:FlxState):FlxState { + findRootComponent()._state = value; + return value; + } + + // lets cache certain items so we dont have to loop multiple times per frame private var _cachedScreenX:Null = null; private var _cachedScreenY:Null = null; @@ -90,32 +91,19 @@ class ComponentImpl extends ComponentBase { if (_cachedScreenX != null && _cachedScreenY != null) { return; } - - var c:Component = asComponent; - var xpos:Float = 0; - var ypos:Float = 0; - while (c != null) { - xpos += c.left; - ypos += c.top; - if (c.componentClipRect != null) { - xpos -= c.componentClipRect.left; - ypos -= c.componentClipRect.top; - } - c = c.parentComponent; - } - - _cachedScreenX = xpos * Toolkit.scaleX; - _cachedScreenY = ypos * Toolkit.scaleY; + var screenBounds = asComponent.screenBounds; + _cachedScreenX = screenBounds.left; + _cachedScreenY = screenBounds.top; } - private var screenX(get, null):Float; - private function get_screenX():Float { + private var cachedScreenX(get, null):Float; + private function get_cachedScreenX():Float { cacheScreenPos(); return _cachedScreenX; } - private var screenY(get, null):Float; - private function get_screenY():Float { + private var cachedScreenY(get, null):Float; + private function get_cachedScreenY():Float { cacheScreenPos(); return _cachedScreenY; } @@ -171,8 +159,8 @@ class ComponentImpl extends ComponentBase { } var b:Bool = false; - var sx = screenX; - var sy = screenY; + var sx = cachedScreenX; + var sy = cachedScreenY; var cx = asComponent.componentWidth * Toolkit.scaleX; var cy = asComponent.componentHeight * Toolkit.scaleY; @@ -185,8 +173,8 @@ class ComponentImpl extends ComponentBase { var clip:Component = findClipComponent(); if (clip != null) { b = false; - var sx = (clip.screenX + (clip.componentClipRect.left * Toolkit.scaleX)); - var sy = (clip.screenY + (clip.componentClipRect.top * Toolkit.scaleY)); + var sx = (clip.cachedScreenX + (clip.componentClipRect.left * Toolkit.scaleX)); + var sy = (clip.cachedScreenY + (clip.componentClipRect.top * Toolkit.scaleY)); var cx = clip.componentClipRect.width * Toolkit.scaleX; var cy = clip.componentClipRect.height * Toolkit.scaleY; if (x >= sx && y >= sy && x <= sx + cx && y < sy + cy) { @@ -204,10 +192,10 @@ class ComponentImpl extends ComponentBase { if (parentComponent == null) { if (left != null) { - this.x = left; + //this.x = left; } if (top != null) { - this.y = top; + //this.y = top; } } } @@ -225,10 +213,9 @@ class ComponentImpl extends ComponentBase { var h:Int = Std.int(height * Toolkit.scaleY); if (_surface.width != w || _surface.height != h) { if (w <= 0 || h <= 0) { - _surface.graphic.destroy(); _surface.makeGraphic(1, 1, 0x0, true); + _surface.visible = false; } else { - _surface.graphic.destroy(); _surface.makeGraphic(w, h, 0x0, true); applyStyle(style); } @@ -239,6 +226,10 @@ class ComponentImpl extends ComponentBase { // Display tree //*********************************************************************************************************** + private override function handleDestroy() { + destroyInternal(); + } + private override function handleSetComponentIndex(child:Component, index:Int) { handleAddComponentAt(child, index); } @@ -248,18 +239,10 @@ class ComponentImpl extends ComponentBase { return child; } - // TODO: really need revision on if this is still needed, or what purpose it was originally supposed - // to serve, maybe its no longer needed in flixel 5.0? - private var _overrideSkipTransformChildren:Bool = true; private function superVisible(value:Bool) { - if (_overrideSkipTransformChildren) { - _skipTransformChildren = true; - } + _skipTransformChildren = true; super.set_visible(value); _skipTransformChildren = false; - if (_overrideSkipTransformChildren) { - _skipTransformChildren = false; - } } private override function handleAddComponentAt(child:Component, index:Int):Component { @@ -284,11 +267,17 @@ class ComponentImpl extends ComponentBase { _unsolicitedMembers = []; } if (findUnsolictedEntryFromSprite(sprite) == null) { - _unsolicitedMembers.push({ - sprite: sprite, - originalX: sprite.x, - originalY: sprite.y - }); + var use = true; + if (_textInput != null && _textInput.equals(sprite)) { + use = false; + } + if (use) { + _unsolicitedMembers.push({ + sprite: sprite, + originalX: sprite.x, + originalY: sprite.y + }); + } } } super.preAdd(sprite); @@ -350,13 +339,11 @@ class ComponentImpl extends ComponentBase { _textDisplay.tf.visible = show; } if (hasTextInput()) { - _textInput.tf.visible = show; + _textInput.visible = show; } for (c in this.childComponents) { - if (!c.hidden) { - c.applyVisibility(show); - } + c.applyVisibility(show && !c.hidden); } } @@ -377,7 +364,7 @@ class ComponentImpl extends ComponentBase { } */ - FlxStyleHelper.applyStyle(_surface, style); + _surface.visible = FlxStyleHelper.applyStyle(_surface, style); applyFilters(style); } @@ -387,20 +374,30 @@ class ComponentImpl extends ComponentBase { getTextDisplay().tf.alpha = value; } if (hasTextInput()) { - getTextInput().tf.alpha = value; + getTextInput().alpha = value; + } + if (hasImageDisplay()) { + getImageDisplay().alpha = value; } for (c in childComponents) { - c.applyAlpha(value); + if (c.style != null && c.style.opacity != null) { + c.applyAlpha(c.style.opacity * value); + } else { + c.applyAlpha(value); + } } } public override function set_alpha(alpha:Float):Float { _surface.alpha = alpha; if (hasTextDisplay()) { - getTextDisplay().tf.alpha = value; + getTextDisplay().tf.alpha = alpha; } if (hasTextInput()) { - getTextInput().tf.alpha = alpha; + getTextInput().alpha = alpha; + } + if (hasImageDisplay()) { + getImageDisplay().alpha = alpha; } return super.set_alpha(alpha); } @@ -429,7 +426,9 @@ class ComponentImpl extends ComponentBase { _imageDisplay.visible = false; add(_imageDisplay); Toolkit.callLater(function() { // lets show it a frame later so its had a chance to reposition - _imageDisplay.visible = true; + if (_imageDisplay != null) { + _imageDisplay.visible = true; + } }); } @@ -449,101 +448,51 @@ class ComponentImpl extends ComponentBase { //*********************************************************************************************************** private override function mapEvent(type:String, listener:UIEvent->Void) { switch (type) { - case MouseEvent.MOUSE_MOVE: - if (_eventMap.exists(MouseEvent.MOUSE_MOVE) == false) { - notifyMouseMove(true); - _eventMap.set(MouseEvent.MOUSE_MOVE, listener); - } - - case MouseEvent.MOUSE_OVER: - if (_eventMap.exists(MouseEvent.MOUSE_OVER) == false) { - notifyMouseMove(true); - _eventMap.set(MouseEvent.MOUSE_OVER, listener); - } - - case MouseEvent.MOUSE_OUT: - if (_eventMap.exists(MouseEvent.MOUSE_OUT) == false) { - notifyMouseMove(true); - _eventMap.set(MouseEvent.MOUSE_OUT, listener); - } - case MouseEvent.MOUSE_DOWN: if (_eventMap.exists(MouseEvent.MOUSE_DOWN) == false) { - notifyMouseDown(true); - notifyMouseUp(true); - notifyMouseMove(true); - _eventMap.set(MouseEvent.MOUSE_DOWN, listener); if (hasTextInput()) { - getTextInput().tf.addEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseEvent); + _eventMap.set(MouseEvent.MOUSE_DOWN, listener); + getTextInput().onMouseDown = __onTextInputMouseEvent; } } case MouseEvent.MOUSE_UP: if (_eventMap.exists(MouseEvent.MOUSE_UP) == false) { - notifyMouseDown(true); - notifyMouseUp(true); - notifyMouseMove(true); - _eventMap.set(MouseEvent.MOUSE_UP, listener); if (hasTextInput()) { - getTextInput().tf.addEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseEvent); + _eventMap.set(MouseEvent.MOUSE_UP, listener); + getTextInput().onMouseUp = __onTextInputMouseEvent; } } - case MouseEvent.MOUSE_WHEEL: - if (_eventMap.exists(MouseEvent.MOUSE_WHEEL) == false) { - notifyMouseMove(true); - notifyMouseWheel(true); - _eventMap.set(MouseEvent.MOUSE_WHEEL, listener); - } - case MouseEvent.CLICK: if (_eventMap.exists(MouseEvent.CLICK) == false) { - notifyMouseDown(true); - notifyMouseUp(true); - notifyMouseMove(true); - _eventMap.set(MouseEvent.CLICK, listener); if (hasTextInput()) { - getTextInput().tf.addEventListener(openfl.events.MouseEvent.CLICK, __onTextInputMouseEvent); + _eventMap.set(MouseEvent.CLICK, listener); + getTextInput().onClick = __onTextInputMouseEvent; } } - - case MouseEvent.DBL_CLICK: - if (_eventMap.exists(MouseEvent.DBL_CLICK) == false) { - notifyMouseDown(true); - notifyMouseUp(true); - notifyMouseMove(true); - _eventMap.set(MouseEvent.DBL_CLICK, listener); - } - - case MouseEvent.RIGHT_MOUSE_DOWN: - if (_eventMap.exists(MouseEvent.RIGHT_MOUSE_DOWN) == false) { - notifyMouseDown(true); - notifyMouseUp(true); - notifyMouseMove(true); - _eventMap.set(MouseEvent.RIGHT_MOUSE_DOWN, listener); - } - case MouseEvent.RIGHT_MOUSE_UP: - if (_eventMap.exists(MouseEvent.RIGHT_MOUSE_UP) == false) { - notifyMouseDown(true); - notifyMouseUp(true); - notifyMouseMove(true); - _eventMap.set(MouseEvent.RIGHT_MOUSE_UP, listener); + case KeyboardEvent.KEY_DOWN: + if (_eventMap.exists(KeyboardEvent.KEY_DOWN) == false) { + if (hasTextInput()) { + _eventMap.set(KeyboardEvent.KEY_DOWN, listener); + getTextInput().onKeyDown = __onTextInputKeyboardEvent; + } } - - case MouseEvent.RIGHT_CLICK: - if (_eventMap.exists(MouseEvent.RIGHT_CLICK) == false) { - notifyMouseDown(true); - notifyMouseUp(true); - notifyMouseMove(true); - _eventMap.set(MouseEvent.RIGHT_CLICK, listener); + + case KeyboardEvent.KEY_UP: + if (_eventMap.exists(KeyboardEvent.KEY_UP) == false) { + if (hasTextInput()) { + _eventMap.set(KeyboardEvent.KEY_UP, listener); + getTextInput().onKeyUp = __onTextInputKeyboardEvent; + } } case UIEvent.CHANGE: if (_eventMap.exists(UIEvent.CHANGE) == false) { if (hasTextInput() == true) { _eventMap.set(UIEvent.CHANGE, listener); - getTextInput().tf.addEventListener(Event.CHANGE, __onTextInputChange); + getTextInput().onChange = __onTextInputChange; } } } @@ -551,158 +500,52 @@ class ComponentImpl extends ComponentBase { private override function unmapEvent(type:String, listener:UIEvent->Void) { switch (type) { - case MouseEvent.MOUSE_MOVE: - _eventMap.remove(type); - notifyMouseMove(false); - - case MouseEvent.MOUSE_OVER: - _eventMap.remove(type); - notifyMouseMove(false); - - case MouseEvent.MOUSE_OUT: - _eventMap.remove(type); - notifyMouseMove(false); - case MouseEvent.MOUSE_DOWN: - _eventMap.remove(type); - notifyMouseDown(false); - notifyMouseUp(false); - notifyMouseMove(false); if (hasTextInput()) { - getTextInput().tf.removeEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseEvent); + _eventMap.remove(type); + getTextInput().onMouseDown = null; } - case MouseEvent.MOUSE_UP: - _eventMap.remove(type); - notifyMouseDown(false); - notifyMouseUp(false); - notifyMouseMove(false); if (hasTextInput()) { - getTextInput().tf.removeEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseEvent); + _eventMap.remove(type); + getTextInput().onMouseUp = null; } - case MouseEvent.MOUSE_WHEEL: - _eventMap.remove(type); - notifyMouseMove(false); - notifyMouseWheel(false); - case MouseEvent.CLICK: - _eventMap.remove(type); - notifyMouseDown(false); - notifyMouseUp(false); - notifyMouseMove(false); if (hasTextInput()) { - getTextInput().tf.removeEventListener(openfl.events.MouseEvent.CLICK, __onTextInputMouseEvent); + _eventMap.remove(type); + getTextInput().onClick = null; } - - case MouseEvent.DBL_CLICK: - _eventMap.remove(type); - notifyMouseDown(false); - notifyMouseUp(false); - notifyMouseMove(false); - - case MouseEvent.RIGHT_MOUSE_DOWN: - _eventMap.remove(type); - notifyMouseDown(false); - notifyMouseUp(false); - notifyMouseMove(false); - case MouseEvent.RIGHT_MOUSE_UP: - _eventMap.remove(type); - notifyMouseDown(false); - notifyMouseUp(false); - notifyMouseMove(false); - - case MouseEvent.RIGHT_CLICK: - _eventMap.remove(type); - notifyMouseDown(false); - notifyMouseUp(false); - notifyMouseMove(false); + case KeyboardEvent.KEY_DOWN: + if (hasTextInput()) { + _eventMap.remove(type); + getTextInput().onKeyDown = null; + } + + case KeyboardEvent.KEY_UP: + if (hasTextInput()) { + _eventMap.remove(type); + getTextInput().onKeyUp = null; + } case UIEvent.CHANGE: - _eventMap.remove(type); - if (hasTextInput() == true) { - getTextInput().tf.removeEventListener(Event.CHANGE, __onTextInputChange); + if (hasTextInput()) { + _eventMap.remove(type); + getTextInput().onChange = null; } } } - - private var _counterNotifyMouseDown:Int = 0; - private function notifyMouseDown(notify:Bool) { - if (notify == true) { - _counterNotifyMouseDown++; - } else { - _counterNotifyMouseDown--; - if (_counterNotifyMouseDown < 0) { - _counterNotifyMouseDown = 0; - } - } - if (notify == true && _counterNotifyMouseDown == 1) { - MouseHelper.notify(MouseEvent.MOUSE_DOWN, __onMouseDown); - } else if (notify == false && _counterNotifyMouseDown == 0) { - MouseHelper.remove(MouseEvent.MOUSE_DOWN, __onMouseDown); - } - } - - private var _counterNotifyMouseUp:Int = 0; - private function notifyMouseUp(notify:Bool) { - if (notify == true) { - _counterNotifyMouseUp++; - } else { - _counterNotifyMouseUp--; - if (_counterNotifyMouseUp < 0) { - _counterNotifyMouseUp = 0; - } - } - if (notify == true && _counterNotifyMouseUp == 1) { - MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp); - } else if (notify == false && _counterNotifyMouseUp == 0) { - MouseHelper.remove(MouseEvent.MOUSE_UP, __onMouseUp); - } - } - - private var _counterNotifyMouseMove:Int = 0; - private function notifyMouseMove(notify:Bool) { - if (notify == true) { - _counterNotifyMouseMove++; - } else { - _counterNotifyMouseMove--; - if (_counterNotifyMouseMove < 0) { - _counterNotifyMouseMove = 0; - } - } - if (notify == true && _counterNotifyMouseMove == 1) { - MouseHelper.notify(MouseEvent.MOUSE_MOVE, __onMouseMove); - } else if (notify == false && _counterNotifyMouseMove == 0) { - MouseHelper.remove(MouseEvent.MOUSE_MOVE, __onMouseMove); - } - } - - private var _counterNotifyMouseWheel:Int = 0; - private function notifyMouseWheel(notify:Bool) { - if (notify == true) { - _counterNotifyMouseWheel++; - } else { - _counterNotifyMouseWheel--; - } - if (notify == true && _counterNotifyMouseWheel == 1) { - MouseHelper.notify(MouseEvent.MOUSE_WHEEL, __onMouseWheel); - } else if (notify == false && _counterNotifyMouseWheel == 0) { - MouseHelper.remove(MouseEvent.MOUSE_WHEEL, __onMouseWheel); - } - } - private function __onTextInputChange(event:Event) { + private function __onTextInputChange(event:TextInputEvent) { var fn:UIEvent->Void = _eventMap.get(UIEvent.CHANGE); if (fn != null) { fn(new UIEvent(UIEvent.CHANGE)); } } - // since we use openfl's text input for text input we need to handle its events differently - // to how we do in the rest of haxeui-flixel - private function __onTextInputMouseEvent(event:openfl.events.MouseEvent) { + private function __onTextInputMouseEvent(event:TextInputEvent) { var type = null; switch (event.type) { case openfl.events.MouseEvent.MOUSE_DOWN: @@ -724,295 +567,44 @@ class ComponentImpl extends ComponentBase { } } + /* private var _mouseOverFlag:Bool = false; private var _cursorSet:Bool = false; - private function __onMouseMove(event:MouseEvent) { - var x = event.screenX; - var y = event.screenY; - lastMouseX = x; - lastMouseY = y; - - var hasMember = StateHelper.hasMember(_surface); - - if (Platform.instance.isMobile == false) { - if (_mouseOverFlag == true) { - if (hasMember == false) { - _mouseOverFlag = false; - var fn:UIEvent->Void = _eventMap.get(haxe.ui.events.MouseEvent.MOUSE_OUT); - if (fn != null) { - var mouseEvent = new haxe.ui.events.MouseEvent(haxe.ui.events.MouseEvent.MOUSE_OUT); - mouseEvent.screenX = x / Toolkit.scaleX; - mouseEvent.screenY = y / Toolkit.scaleY; - #if mobile - mouseEvent.touchEvent = true; - #end - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - return; - } - } - - if (hasMember == false) { - return; - } - - var i = inBounds(x, y); - if (i == true) { - if (this.style != null && this.style.cursor != null) { - Screen.instance.setCursor(this.style.cursor, this.style.cursorOffsetX, this.style.cursorOffsetY); - _cursorSet = true; - } - - var fn:UIEvent->Void = _eventMap.get(haxe.ui.events.MouseEvent.MOUSE_MOVE); - if (fn != null) { - var mouseEvent = new haxe.ui.events.MouseEvent(haxe.ui.events.MouseEvent.MOUSE_MOVE); - mouseEvent.screenX = x / Toolkit.scaleX; - mouseEvent.screenY = y / Toolkit.scaleY; - #if mobile - mouseEvent.touchEvent = true; - #end - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - } - - if (i == true && _mouseOverFlag == false) { - if (isEventRelevant(getComponentsAtPoint(x, y, true), MouseEvent.MOUSE_OVER)) { - if (this.style != null && this.style.cursor != null) { - Screen.instance.setCursor(this.style.cursor, this.style.cursorOffsetX, this.style.cursorOffsetY); - _cursorSet = true; - } - - _mouseOverFlag = true; - var fn:UIEvent->Void = _eventMap.get(haxe.ui.events.MouseEvent.MOUSE_OVER); - if (fn != null) { - var mouseEvent = new haxe.ui.events.MouseEvent(haxe.ui.events.MouseEvent.MOUSE_OVER); - mouseEvent.screenX = x / Toolkit.scaleX; - mouseEvent.screenY = y / Toolkit.scaleY; - #if mobile - mouseEvent.touchEvent = true; - #end - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - } - } else if (i == false && _mouseOverFlag == true) { - if (_cursorSet) { - Screen.instance.setCursor("default"); - _cursorSet = false; - } - - _mouseOverFlag = false; - var fn:UIEvent->Void = _eventMap.get(haxe.ui.events.MouseEvent.MOUSE_OUT); - if (fn != null) { - var mouseEvent = new haxe.ui.events.MouseEvent(haxe.ui.events.MouseEvent.MOUSE_OUT); - mouseEvent.screenX = x / Toolkit.scaleX; - mouseEvent.screenY = y / Toolkit.scaleY; - #if mobile - mouseEvent.touchEvent = true; - #end - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - } - } + private function __onMouseOver(event:MouseEvent) { + _mouseOverFlag = true; } - private var _mouseDownFlag:Bool = false; - private var _mouseDownButton:Int = -1; - private function __onMouseDown(event:MouseEvent) { - if (Platform.instance.isMobile == false) { - if (_mouseOverFlag == false) { - return; - } - } - - - if (StateHelper.hasMember(_surface) == false) { - return; - } - - var button:Int = event.data; - var x = event.screenX; - var y = event.screenY; - lastMouseX = x; - lastMouseY = y; - var i = inBounds(x, y); - if (i == true && _mouseDownFlag == false) { - /* - if (hasComponentOver(cast this, x, y) == true) { - return; - } - */ - if (isEventRelevant(getComponentsAtPoint(x, y, true), MouseEvent.MOUSE_DOWN)) { - _mouseDownFlag = true; - _mouseDownButton = button; - var type = button == 0 ? haxe.ui.events.MouseEvent.MOUSE_DOWN: haxe.ui.events.MouseEvent.RIGHT_MOUSE_DOWN; - var fn:UIEvent->Void = _eventMap.get(type); - if (fn != null) { - var mouseEvent = new haxe.ui.events.MouseEvent(type); - mouseEvent.screenX = x / Toolkit.scaleX; - mouseEvent.screenY = y / Toolkit.scaleY; - if (Platform.instance.isMobile) { - mouseEvent.touchEvent = true; - } - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - } - } - } - - private function __onMouseUp(event:MouseEvent) { - if (Platform.instance.isMobile == false) { - if (_mouseOverFlag == false) { - //return; - } - } - - if (StateHelper.hasMember(_surface) == false) { - return; - } - - var button:Int = _mouseDownButton; - var x = event.screenX; - var y = event.screenY; - - lastMouseX = x; - lastMouseY = y; - var i = inBounds(x, y); - if (i == true) { - /* - if (hasComponentOver(cast this, x, y) == true) { - return; - } - */ - - if (_mouseDownFlag == true) { - var type = button == 0 ? haxe.ui.events.MouseEvent.CLICK: haxe.ui.events.MouseEvent.RIGHT_CLICK; - var fn:UIEvent->Void = _eventMap.get(type); - if (fn != null) { - var mouseEvent = new haxe.ui.events.MouseEvent(type); - mouseEvent.screenX = x / Toolkit.scaleX; - mouseEvent.screenY = y / Toolkit.scaleY; - if (Platform.instance.isMobile) { - mouseEvent.touchEvent = true; - } - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - - if (type == haxe.ui.events.MouseEvent.CLICK) { - _lastClickTimeDiff = Timer.stamp() - _lastClickTime; - _lastClickTime = Timer.stamp(); - if (_lastClickTimeDiff >= 0.5) { // 0.5 seconds - _lastClickX = x; - _lastClickY = y; - } - } - } - - _mouseDownFlag = false; - var type = button == 0 ? haxe.ui.events.MouseEvent.MOUSE_UP: haxe.ui.events.MouseEvent.RIGHT_MOUSE_UP; - var fn:UIEvent->Void = _eventMap.get(type); - if (fn != null) { - var mouseEvent = new haxe.ui.events.MouseEvent(type); - mouseEvent.screenX = x / Toolkit.scaleX; - mouseEvent.screenY = y / Toolkit.scaleY; - if (Platform.instance.isMobile) { - mouseEvent.touchEvent = true; - } - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - } - _mouseDownFlag = false; + private function __onMouseOut(event:MouseEvent) { + _mouseOverFlag = false; } + */ #if haxeui_dont_impose_base_class private function applyRootLayout(l:String) { } #end - - private function __onDoubleClick(event:MouseEvent) { - if (StateHelper.hasMember(_surface) == false) { - return; - } - - var button:Int = _mouseDownButton; - var x = event.screenX; - var y = event.screenY; - - lastMouseX = x; - lastMouseY = y; - var i = inBounds(x, y); - if (i == true && button == 0) { - /* - if (hasComponentOver(cast this, x, y) == true) { - return; - } - */ - - _mouseDownFlag = false; - var mouseDelta:Float = MathUtil.distance(x, y, _lastClickX, _lastClickY); - if (_lastClickTimeDiff < 0.5 && mouseDelta < 5) { // 0.5 seconds - var type = haxe.ui.events.MouseEvent.DBL_CLICK; - var fn:UIEvent->Void = _eventMap.get(type); - if (fn != null) { - var mouseEvent = new haxe.ui.events.MouseEvent(type); - mouseEvent.screenX = x / Toolkit.scaleX; - mouseEvent.screenY = y / Toolkit.scaleY; - if (Platform.instance.isMobile) { - mouseEvent.touchEvent = true; - } - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - } - } - _mouseDownFlag = false; - } - private function __onMouseWheel(event:MouseEvent) { - if (StateHelper.hasMember(_surface) == false) { - return; + private function __onTextInputKeyboardEvent(event:openfl.events.KeyboardEvent) { + var type = switch (event.type) { + case openfl.events.KeyboardEvent.KEY_DOWN: + KeyboardEvent.KEY_DOWN; + case openfl.events.KeyboardEvent.KEY_UP: + KeyboardEvent.KEY_UP; + default: + null; } - - var delta = event.delta; - var fn = _eventMap.get(MouseEvent.MOUSE_WHEEL); + var fn = _eventMap.get(type); if (fn == null) { return; } - if (!inBounds(lastMouseX, lastMouseY)) { - return; - } - - var mouseEvent = new MouseEvent(MouseEvent.MOUSE_WHEEL); - mouseEvent.screenX = lastMouseX / Toolkit.scaleX; - mouseEvent.screenY = lastMouseY / Toolkit.scaleY; - mouseEvent.delta = Math.max(-1, Math.min(1, -delta)); - if (Platform.instance.isMobile) { - mouseEvent.touchEvent = true; - } - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - - private function isEventRelevant(children:Array, eventType:String):Bool { - var relevant = false; - for (c in children) { - if (c == this) { - relevant = true; - } - if (c.parentComponent == null) { - break; - } - } - - return relevant; + var keyboardEvent = new KeyboardEvent(type); + keyboardEvent.keyCode = event.keyCode; + keyboardEvent.altKey = event.altKey; + keyboardEvent.ctrlKey = event.ctrlKey; + keyboardEvent.shiftKey = event.shiftKey; + fn(keyboardEvent); } //*********************************************************************************************************** @@ -1036,8 +628,8 @@ class ComponentImpl extends ComponentBase { if (_textInput == null) { super.createTextInput(text); _textInput.attach(); - _textInput.tf.visible = false; - FlxG.addChildBelowMouse(_textInput.tf); + _textInput.visible = false; + _textInput.addToComponent(cast this); /* Toolkit.callLater(function() { // lets show it a frame later so its had a chance to reposition if (_textInput != null) { @@ -1053,50 +645,41 @@ class ComponentImpl extends ComponentBase { // Util //*********************************************************************************************************** private function repositionChildren() { + var xpos = this.cachedScreenX; + var ypos = this.cachedScreenY; if (_surface != null) { - _surface.x = this.screenX; - _surface.y = this.screenY; + _surface.x = xpos; + _surface.y = ypos; } if (_textDisplay != null) { - #if html5 - var offsetX = 1 / Toolkit.scaleX; - var offsetY = 1 / Toolkit.scaleY; - #else var offsetX = 2 / Toolkit.scaleX; var offsetY = 2 / Toolkit.scaleY; - #end - _textDisplay.tf.x = _surface.x + _textDisplay.left - offsetX; - _textDisplay.tf.y = _surface.y + _textDisplay.top - offsetY; + _textDisplay.tf.x = xpos + _textDisplay.left - offsetX; + _textDisplay.tf.y = ypos + _textDisplay.top - offsetY; } if (_textInput != null) { - #if html5 - var offsetX = 1 / Toolkit.scaleX; - var offsetY = 1 / Toolkit.scaleY; - #else var offsetX = 2 / Toolkit.scaleX; var offsetY = 2 / Toolkit.scaleY; - #end - - _textInput.tf.x = (_surface.x + _textInput.left - offsetX) * FlxG.scaleMode.scale.x; - _textInput.tf.y = (_surface.y + _textInput.top - offsetY) * FlxG.scaleMode.scale.y; - _textInput.tf.scaleX = FlxG.scaleMode.scale.x; - _textInput.tf.scaleY = FlxG.scaleMode.scale.y; + _textInput.x = (xpos + _textInput.left - offsetX); + _textInput.y = (ypos + _textInput.top - offsetY); + _textInput.scaleX = FlxG.scaleMode.scale.x; + _textInput.scaleY = FlxG.scaleMode.scale.y; _textInput.update(); } if (_imageDisplay != null) { var offsetX = 0; var offsetY = 0; - _imageDisplay.x = _surface.x + _imageDisplay.left - offsetX; - _imageDisplay.y = _surface.y + _imageDisplay.top - offsetY; + _imageDisplay.x = xpos + _imageDisplay.left - offsetX; + _imageDisplay.y = ypos + _imageDisplay.top - offsetY; } if (_unsolicitedMembers != null) { for (m in _unsolicitedMembers) { - m.sprite.x = m.originalX + this.screenX; - m.sprite.y = m.originalY + this.screenY; + m.sprite.x = m.originalX + this.cachedScreenX; + m.sprite.y = m.originalY + this.cachedScreenY; } } } @@ -1172,20 +755,20 @@ class ComponentImpl extends ComponentBase { // Flixel overrides //*********************************************************************************************************** - private var _updates:Int = 0; + private var _updates:Float = 0; public override function update(elapsed:Float) { if (_destroyed) { super.update(elapsed); return; } if (_destroy == true) { + clearCaches(); destroyInternal(); super.update(elapsed); return; } clearCaches(); - applyClipRect(); repositionChildren(); _updates++; @@ -1198,25 +781,57 @@ class ComponentImpl extends ComponentBase { } super.update(elapsed); + + // only the root component will start the applyClipRect() chain + if (parentComponent == null) { + // this needs to be called after super.update() so the children's positions + // get updated before clipping them + applyClipRect(); + } } private function applyClipRect() { - if (this.componentClipRect != null) { - var value = this.componentClipRect; - value.top = Std.int(value.top); - value.left = Std.int(value.left); - if (parentComponent != null) { - var rect = FlxRect.get((value.left * Toolkit.scaleX) + _surface.x - parentComponent.x, - (value.top * Toolkit.scaleY) + _surface.y - parentComponent.y, - (value.width * Toolkit.scaleX), (value.height * Toolkit.scaleY)); - clipRect = rect; - rect.put(); - } else { // top-level (root) components can also clip (Absolute auto clips via a css style), but this means they wont have a parentComponent set, lets handle them differently - var rect = FlxRect.get(_surface.x, _surface.y, value.width, value.height); - clipRect = rect; - rect.put(); + if (componentClipRect != null) { + clipRect = getFlixelClipRect(clipRect); + } + + for (c in childComponents) { + c.applyClipRect(); + } + } + + private function getFlixelClipRect(?rect:FlxRect):FlxRect { + var value = componentClipRect; + if (value == null) { + return null; + } + + value.top = Std.int(value.top); + value.left = Std.int(value.left); + + if (rect == null) { + rect = FlxRect.get(); + } + rect.set((value.left * Toolkit.scaleX) + _surface.x - x, + (value.top * Toolkit.scaleY) + _surface.y - y, + (value.width * Toolkit.scaleX), (value.height * Toolkit.scaleY)); + + // find the nearest parent with a clip rect so we can intersect it with + // the one from this component + var p = parentComponent; + while (p != null) { + if (p.componentClipRect != null) { + var pRect = p.getFlixelClipRect(); + var oldRect = rect; + rect = rect.intersection(pRect); + pRect.put(); + oldRect.put(); + break; } + p = p.parentComponent; } + + return rect; } // these functions (applyAddInternal / applyRemoveInternal) are called when a component is added / removed @@ -1225,8 +840,12 @@ class ComponentImpl extends ComponentBase { // application, this means that when things are removed from the screen (and not destroyed) it can leave them // behind private function applyAddInternal() { + if (!TextInputImpl.USE_ON_ADDED) { + return; + } + if (hasTextInput() && asComponent.hidden == false) { - getTextInput().tf.visible = true; + getTextInput().visible = true; } for (c in childComponents) { c.applyAddInternal(); @@ -1234,8 +853,12 @@ class ComponentImpl extends ComponentBase { } private function applyRemoveInternal() { + if (!TextInputImpl.USE_ON_REMOVED) { + return; + } + if (hasTextInput()) { - getTextInput().tf.visible = false; + getTextInput().visible = false; } for (c in childComponents) { c.applyRemoveInternal(); @@ -1244,9 +867,6 @@ class ComponentImpl extends ComponentBase { private var _destroyed:Bool = false; private function destroyInternal() { - if (!_allowDestroy) { - return; - } if (_surface != null) { _surface.destroy(); _surface = null; @@ -1258,7 +878,7 @@ class ComponentImpl extends ComponentBase { } if (_textInput != null) { - _textInput.destroy(); + _textInput.destroy(cast this); _textInput = null; } @@ -1271,17 +891,13 @@ class ComponentImpl extends ComponentBase { _unsolicitedMembers = null; } + _state = null; _destroy = false; _destroyed = true; super.destroy(); } - private var _allowDestroy:Bool = true; public override function destroy():Void { - if (!_allowDestroy) { - return; - } - if (parentComponent != null) { if (parentComponent.getComponentIndex(cast this) != -1) { parentComponent.removeComponent(cast this); @@ -1296,7 +912,7 @@ class ComponentImpl extends ComponentBase { private override function set_x(value:Float):Float { var r = super.set_x(value); - if (this.parentComponent == null) { + if (this.parentComponent == null && _surface != null) { this.left = value; } return r; @@ -1304,7 +920,7 @@ class ComponentImpl extends ComponentBase { private override function set_y(value:Float):Float { var r = super.set_y(value); - if (this.parentComponent == null) { + if (this.parentComponent == null && _surface != null) { this.top = value; } return r; diff --git a/haxe/ui/backend/ComponentSurface.hx b/haxe/ui/backend/ComponentSurface.hx index 934a1b2..c15ef02 100644 --- a/haxe/ui/backend/ComponentSurface.hx +++ b/haxe/ui/backend/ComponentSurface.hx @@ -1,3 +1,3 @@ package haxe.ui.backend; -typedef ComponentSurface = flixel.group.FlxSpriteGroup; \ No newline at end of file +typedef ComponentSurface = flixel.group.FlxSpriteContainer; \ No newline at end of file diff --git a/haxe/ui/backend/ImageDisplayImpl.hx b/haxe/ui/backend/ImageDisplayImpl.hx index 5fbc096..c31ecec 100644 --- a/haxe/ui/backend/ImageDisplayImpl.hx +++ b/haxe/ui/backend/ImageDisplayImpl.hx @@ -1,12 +1,14 @@ package haxe.ui.backend; import flixel.graphics.frames.FlxImageFrame; +import flixel.math.FlxRect; import haxe.ui.Toolkit; class ImageDisplayImpl extends ImageBase { public function new() { super(); this.pixelPerfectRender = true; + this.active = false; } private override function validateData():Void { @@ -14,16 +16,25 @@ class ImageDisplayImpl extends ImageBase { frames = FlxImageFrame.fromFrame(_imageInfo.data); aspectRatio = _imageInfo.width / _imageInfo.height; - - width = frameWidth = Std.int(_imageInfo.width * Toolkit.scaleX); - height = frameHeight = Std.int(_imageInfo.height * Toolkit.scaleY); + + origin.set(0, 0); } } private override function validateDisplay() { var scaleX:Float = _imageWidth / (_imageInfo.width / Toolkit.scaleX); var scaleY:Float = _imageHeight / (_imageInfo.height / Toolkit.scaleY); - origin.set(0, 0); scale.set(scaleX, scaleY); + + width = Math.abs(scaleX) * frameWidth; + height = Math.abs(scaleY) * frameHeight; + } + + override function set_clipRect(rect:FlxRect):FlxRect { + if (rect != null) { + return super.set_clipRect(FlxRect.get(rect.x / scale.x, rect.y / scale.y, rect.width / scale.x, rect.height / scale.y)); + } else { + return super.set_clipRect(null); + } } } diff --git a/haxe/ui/backend/OpenFileDialogImpl.hx b/haxe/ui/backend/OpenFileDialogImpl.hx index 0db9bdb..57b22cb 100644 --- a/haxe/ui/backend/OpenFileDialogImpl.hx +++ b/haxe/ui/backend/OpenFileDialogImpl.hx @@ -86,9 +86,16 @@ class OpenFileDialogImpl extends OpenFileDialogBase { destroyFileRef(); var infos:Array = []; for (fileRef in fileList) { + + var fullPath:String = null; + #if sys + fullPath = @:privateAccess fileRef.__path; + #end + var info:SelectedFileInfo = { isBinary: false, - name: fileRef.name + name: fileRef.name, + fullPath: fullPath } if (options.readContents == true) { _refToInfo.set(fileRef, info); diff --git a/haxe/ui/backend/PlatformImpl.hx b/haxe/ui/backend/PlatformImpl.hx index 32ce443..7f77666 100644 --- a/haxe/ui/backend/PlatformImpl.hx +++ b/haxe/ui/backend/PlatformImpl.hx @@ -1,4 +1,9 @@ package haxe.ui.backend; +import openfl.system.Capabilities; + class PlatformImpl extends PlatformBase { + public override function getSystemLocale():String { + return Capabilities.language; + } } \ No newline at end of file diff --git a/haxe/ui/backend/ScreenImpl.hx b/haxe/ui/backend/ScreenImpl.hx index b4e3bf9..7596740 100644 --- a/haxe/ui/backend/ScreenImpl.hx +++ b/haxe/ui/backend/ScreenImpl.hx @@ -3,10 +3,11 @@ package haxe.ui.backend; import flixel.FlxBasic; import flixel.FlxG; import flixel.FlxSprite; -import flixel.group.FlxGroup.FlxTypedGroup; -import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; +import flixel.group.FlxGroup; +import flixel.group.FlxSpriteGroup; import haxe.ui.Toolkit; import haxe.ui.backend.flixel.CursorHelper; +import haxe.ui.backend.flixel.KeyboardHelper; import haxe.ui.backend.flixel.MouseHelper; import haxe.ui.backend.flixel.StateHelper; import haxe.ui.core.Component; @@ -14,101 +15,84 @@ import haxe.ui.core.Screen; import haxe.ui.events.KeyboardEvent; import haxe.ui.events.MouseEvent; import haxe.ui.events.UIEvent; +import haxe.ui.tooltips.ToolTipManager; import lime.system.System; import openfl.Lib; @:access(haxe.ui.backend.ComponentImpl) class ScreenImpl extends ScreenBase { private var _mapping:MapVoid>; - - #if (flixel < "4.9.0") // subStateOpened / subStateClosed added in 4.9.0 - private static var _inputManager:haxe.ui.backend.flixel.InputManager = null; - #end - + public function new() { _mapping = new MapVoid>(); - #if (flixel < "4.9.0") // subStateOpened / subStateClosed added in 4.9.0 - - if (_inputManager == null) { - _inputManager = new haxe.ui.backend.flixel.InputManager(); - _inputManager.onResetCb = onReset; - FlxG.inputs.add(_inputManager); - } - - #end - + MouseHelper.init(); + KeyboardHelper.init(); + FlxG.signals.postGameStart.add(onPostGameStart); + FlxG.signals.preStateSwitch.add(onPreStateSwitch); FlxG.signals.postStateSwitch.add(onPostStateSwitch); FlxG.signals.preStateCreate.add(onPreStateCreate); onPostStateSwitch(); - + addResizeHandler(); } private function onPreStateCreate(state:flixel.FlxState) { - state.memberAdded.add(onMemberAdded); + if (!state.memberAdded.has(onMemberAdded)) { + state.memberAdded.add(onMemberAdded); + } checkMembers(state); - state.memberRemoved.add(onMemberRemoved); - } - - #if (flixel < "4.9.0") // subStateOpened / subStateClosed added in 4.9.0 - - private function onReset() { - if (FlxG.state != null && FlxG.state.subState != null) { - var cachedSubStateOpenedCallback = FlxG.state.subState.openCallback; - FlxG.state.subState.openCallback = function() { - onMemberAdded(FlxG.state.subState); - if (cachedSubStateOpenedCallback != null) { - cachedSubStateOpenedCallback(); - } - } + if (!state.memberRemoved.has(onMemberRemoved)) { + state.memberRemoved.add(onMemberRemoved); } } - - #end - + private function onPostGameStart() { onPostStateSwitch(); } - - private function onPostStateSwitch() { + + private function onPreStateSwitch() { if (FlxG.game == null) { return; } + ToolTipManager.instance.reset(); + if (rootComponents != null) { + while (rootComponents.length > 0) { + var root = rootComponents[rootComponents.length - 1]; + removeComponent(root); + } + } rootComponents = []; - - #if (flixel >= "4.9.0") // subStateOpened / subStateClosed added in 4.9.0 - FlxG.state.subStateOpened.add(onMemberAdded); - #end - - FlxG.state.memberAdded.add(onMemberAdded); + } + + private function onPostStateSwitch() { + if (FlxG.game == null) { + return; + } + + if (!FlxG.state.subStateOpened.has(onMemberAdded)) { + FlxG.state.subStateOpened.add(onMemberAdded); + } + if (!FlxG.state.memberAdded.has(onMemberAdded)) { + FlxG.state.memberAdded.add(onMemberAdded); + } checkMembers(FlxG.state); - - FlxG.state.memberRemoved.add(onMemberRemoved); - #if (!FLX_NO_MOUSE && !haxeui_no_mouse_reset) - var screen = cast(this, haxe.ui.core.Screen); - screen.registerEvent(MouseEvent.MOUSE_DOWN, function(e:MouseEvent) { - if (screen.hasSolidComponentUnderPoint(e.screenX, e.screenY)) { - FlxG.mouse.reset(); - } - }); - #end + if (!FlxG.state.memberRemoved.has(onMemberRemoved)) { + FlxG.state.memberRemoved.add(onMemberRemoved); + } + if (!FlxG.state.subStateClosed.has(onMemberRemoved)) { + FlxG.state.subStateClosed.add(onMemberRemoved); + } } private function onMemberAdded(m:FlxBasic) { - if ((m is Component) && rootComponents.indexOf(cast(m, Component)) == -1) { + if ((m is Component)) { var c = cast(m, Component); - if (c.percentWidth > 0) { - c.width = (this.width * c.percentWidth) / 100; - } - if (c.percentHeight > 0) { - c.height = (this.height * c.percentHeight) / 100; + if (rootComponents.indexOf(c) == -1) { + addComponent(c); } - rootComponents.push(c); - c.recursiveReady(); - c.syncComponentValidation(); } else if ((m is FlxTypedGroup)) { var group:FlxTypedGroup = cast m; checkMembers(group); @@ -123,20 +107,18 @@ class ScreenImpl extends ScreenBase { } private function checkMembers(state:FlxTypedGroup) { + if (state == null || !state.exists) { + return false; + } + var found = false; // we only want top level components for (m in state.members) { - if ((m is Component) && rootComponents.indexOf(cast(m, Component)) == -1) { + if ((m is Component)) { var c = cast(m, Component); - if (c.percentWidth > 0) { - c.width = (this.width * c.percentWidth) / 100; - } - if (c.percentHeight > 0) { - c.height = (this.height * c.percentHeight) / 100; + if (rootComponents.indexOf(c) == -1) { + addComponent(c); + found = true; } - rootComponents.push(c); - c.recursiveReady(); - c.syncComponentValidation(); - found = true; } else if ((m is FlxTypedGroup)) { var group:FlxTypedGroup = cast m; group.memberAdded.addOnce(onMemberAdded); @@ -155,41 +137,41 @@ class ScreenImpl extends ScreenBase { } return found; } - + private override function get_width():Float { return FlxG.width / Toolkit.scaleX; } - + private override function get_height() { return FlxG.height / Toolkit.scaleY; } - + private override function get_actualWidth():Float { return FlxG.width; } - + private override function get_actualHeight():Float { return FlxG.height; } - + private override function get_dpi():Float { return System.getDisplay(0).dpi; } - + private override function get_title():String { return Lib.current.stage.window.title; } - + private override function set_title(s:String):String { Lib.current.stage.window.title = s; return s; } - + private var _cursor:String = null; public function setCursor(cursor:String, offsetX:Null = null, offsetY:Null = null) { - #if haxeui_flixel_no_custom_cursors + #if (haxeui_flixel_no_custom_cursors || FLX_NO_MOUSE) return; - #end + #else if (!CursorHelper.useCustomCursors) { return; @@ -202,58 +184,72 @@ class ScreenImpl extends ScreenBase { _cursor = cursor; if (CursorHelper.hasCursor(_cursor)) { var cursorInfo = CursorHelper.registeredCursors.get(_cursor); - FlxG.mouse.load(new FlxSprite().loadGraphic(cursorInfo.graphic).pixels, cursorInfo.scale, cursorInfo.offsetX, cursorInfo.offsetY); + FlxG.mouse.load(CursorHelper.mouseLoadFunction(cursorInfo.graphic), cursorInfo.scale, cursorInfo.offsetX, cursorInfo.offsetY); } else if (openfl.Assets.exists(_cursor)) { - FlxG.mouse.load(new FlxSprite().loadGraphic(_cursor).pixels, 1, offsetX, offsetY); + FlxG.mouse.load(CursorHelper.mouseLoadFunction(_cursor), 1, offsetX, offsetY); } else { FlxG.mouse.load(null); } + #end } public override function addComponent(component:Component):Component { + if (rootComponents.contains(component) && StateHelper.currentState.members.contains(component)) { + return component; + } + if (rootComponents.length > 0) { var cameras = StateHelper.findCameras(rootComponents[0]); if (cameras != null) { component.cameras = cameras; } } - + if (StateHelper.currentState.exists == true) { - StateHelper.currentState.add(component); if (rootComponents.indexOf(component) == -1) { rootComponents.push(component); } - component.recursiveReady(); + StateHelper.currentState.add(component); + component.state = StateHelper.currentState; onContainerResize(); + component.recursiveReady(); + component.syncComponentValidation(); component.applyAddInternal(); checkResetCursor(); } return component; } - - public override function removeComponent(component:Component, dispose:Bool = true):Component { + + public override function removeComponent(component:Component, dispose:Bool = true, invalidate:Bool = true):Component { + if (@:privateAccess !component._allowDispose) { + dispose = false; + } if (rootComponents.indexOf(component) == -1) { + if (dispose) { + component.disposeComponent(); + } return component; } rootComponents.remove(component); if (rootComponents.indexOf(component) != -1) { throw "component wasnt actually removed from array, or there is a duplicate in the array"; } + if (StateHelper.currentState.exists == true) { + StateHelper.currentState.remove(component, true); + } + // Destroying a sprite makes it get removed from its container (in this case, the state) without + // being spliced, causing issues later with `addComponent()` not actually adding components on top + // of everything else, so this has to go after the component has been properly removed. if (dispose) { - component.destroyInternal(); - component.destroy(); - component.destroyComponent(); + component.disposeComponent(); } else { component.applyRemoveInternal(); } - if (StateHelper.currentState.exists == true) { - StateHelper.currentState.remove(component, true); - } checkResetCursor(); onContainerResize(); return component; } - + private var _resizeHandlerAdded:Bool = false; private function addResizeHandler() { if (_resizeHandlerAdded == true) { @@ -262,11 +258,11 @@ class ScreenImpl extends ScreenBase { _resizeHandlerAdded = true; FlxG.signals.gameResized.add(onGameResized); } - + private function onGameResized(width:Int, height:Int) { onContainerResize(); } - + private function onContainerResize() { for (c in rootComponents) { if (c.percentWidth > 0) { @@ -277,149 +273,80 @@ class ScreenImpl extends ScreenBase { } } } - + private override function handleSetComponentIndex(child:Component, index:Int) { var offset = 0; StateHelper.currentState.forEach((item) -> { offset++; - }); - - StateHelper.currentState.remove(child); + }); + /* see: https://github.com/haxeui/haxeui-flixel/issues/61 + for (i in 0...StateHelper.currentState.length) { + if ((StateHelper.currentState.members[i] is Component)) { + offset = i; + break; + } + } + */ + + StateHelper.currentState.remove(child, true); StateHelper.currentState.insert(index + offset, child); } - + private override function supportsEvent(type:String):Bool { if (type == MouseEvent.MOUSE_MOVE + || type == MouseEvent.MOUSE_OVER + || type == MouseEvent.MOUSE_OUT || type == MouseEvent.MOUSE_DOWN || type == MouseEvent.MOUSE_UP + || type == MouseEvent.MOUSE_WHEEL + || type == MouseEvent.CLICK + || type == MouseEvent.DBL_CLICK + || type == MouseEvent.RIGHT_CLICK || type == MouseEvent.RIGHT_MOUSE_DOWN || type == MouseEvent.RIGHT_MOUSE_UP + || type == MouseEvent.MIDDLE_CLICK + || type == MouseEvent.MIDDLE_MOUSE_DOWN + || type == MouseEvent.MIDDLE_MOUSE_UP || type == UIEvent.RESIZE || type == KeyboardEvent.KEY_DOWN - || type == KeyboardEvent.KEY_UP - || type == KeyboardEvent.KEY_PRESS) { + || type == KeyboardEvent.KEY_UP) { return true; } return false; } - - private var _mouseDownButton:Int = 0; + private override function mapEvent(type:String, listener:UIEvent->Void) { switch (type) { - case MouseEvent.MOUSE_MOVE: - if (_mapping.exists(type) == false) { - _mapping.set(type, listener); - MouseHelper.notify(MouseEvent.MOUSE_MOVE, __onMouseMove, 10); - } - - case MouseEvent.MOUSE_DOWN | MouseEvent.RIGHT_MOUSE_DOWN: - if (_mapping.exists(type) == false) { - _mapping.set(type, listener); - MouseHelper.notify(MouseEvent.MOUSE_DOWN, __onMouseDown, 10); - } - - case MouseEvent.MOUSE_UP | MouseEvent.RIGHT_MOUSE_UP: - if (_mapping.exists(type) == false) { - _mapping.set(type, listener); - MouseHelper.notify(MouseEvent.MOUSE_UP, __onMouseUp, 10); - } - - case KeyboardEvent.KEY_DOWN: + case MouseEvent.MOUSE_MOVE | MouseEvent.MOUSE_OVER | MouseEvent.MOUSE_OUT | MouseEvent.MOUSE_DOWN | MouseEvent.MOUSE_UP | MouseEvent.MOUSE_WHEEL | MouseEvent.CLICK | MouseEvent.DBL_CLICK | MouseEvent.RIGHT_CLICK | MouseEvent.RIGHT_MOUSE_DOWN | MouseEvent.RIGHT_MOUSE_UP | MouseEvent.MIDDLE_CLICK | MouseEvent.MIDDLE_MOUSE_DOWN | MouseEvent.MIDDLE_MOUSE_UP: if (_mapping.exists(type) == false) { _mapping.set(type, listener); - FlxG.stage.addEventListener(openfl.events.KeyboardEvent.KEY_DOWN, __onKeyEvent); + MouseHelper.notify(type, __onMouseEvent, 10); } - - case KeyboardEvent.KEY_UP: + + case KeyboardEvent.KEY_DOWN | KeyboardEvent.KEY_UP: if (_mapping.exists(type) == false) { _mapping.set(type, listener); - FlxG.stage.addEventListener(openfl.events.KeyboardEvent.KEY_UP, __onKeyEvent); + KeyboardHelper.notify(type, __onKeyEvent, 10); } } } - - private function __onMouseMove(event:MouseEvent) { - var fn = _mapping.get(MouseEvent.MOUSE_MOVE); - if (fn != null) { - var mouseEvent = new MouseEvent(MouseEvent.MOUSE_MOVE); - mouseEvent.screenX = event.screenX / Toolkit.scaleX; - mouseEvent.screenY = event.screenY / Toolkit.scaleY; - mouseEvent.buttonDown = event.data; - #if mobile - mouseEvent.touchEvent = true; - #end - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - } - - private function __onMouseDown(event:MouseEvent) { - var state = FlxG.state; - if (state.subState != null) { - state = state.subState; - } - /* - var contains = containsUnsolicitedMemberAt(event.screenX, event.screenY, state); - if (contains) { // lets attempt not in intercept unsolicated member events - return; - } - */ - var fn = _mapping.get(MouseEvent.MOUSE_DOWN); + private function __onMouseEvent(event:MouseEvent) { + var fn = _mapping.get(event.type); if (fn != null) { - var button:Int = event.data; - var type = button == 0 ? MouseEvent.MOUSE_DOWN: MouseEvent.RIGHT_MOUSE_DOWN; - var mouseEvent = new MouseEvent(type); - mouseEvent.screenX = event.screenX / Toolkit.scaleX; - mouseEvent.screenY = event.screenY / Toolkit.scaleY; - mouseEvent.buttonDown = event.data; - #if mobile - mouseEvent.touchEvent = true; - #end - fn(mouseEvent); - event.canceled = mouseEvent.canceled; + fn(event); } } - - private function __onMouseUp(event:MouseEvent) { - var fn = _mapping.get(MouseEvent.MOUSE_UP); + + private function __onKeyEvent(event:KeyboardEvent) { + var fn = _mapping.get(event.type); if (fn != null) { - var button:Int = event.data; - var type = button == 0 ? MouseEvent.MOUSE_UP: MouseEvent.RIGHT_MOUSE_UP; - var mouseEvent = new MouseEvent(type); - mouseEvent.screenX = event.screenX / Toolkit.scaleX; - mouseEvent.screenY = event.screenY / Toolkit.scaleY; - mouseEvent.buttonDown = event.data; - #if mobile - mouseEvent.touchEvent = true; - #end - fn(mouseEvent); - event.canceled = mouseEvent.canceled; - } - } - - private function __onKeyEvent(event:openfl.events.KeyboardEvent) { - var type:String = null; - if (event.type == openfl.events.KeyboardEvent.KEY_DOWN) { - type = KeyboardEvent.KEY_DOWN; - } else if (event.type == openfl.events.KeyboardEvent.KEY_UP) { - type = KeyboardEvent.KEY_UP; - } - - if (type != null) { - var fn = _mapping.get(type); - if (fn != null) { - var keyboardEvent = new KeyboardEvent(type); - keyboardEvent.keyCode = event.keyCode; - keyboardEvent.ctrlKey = event.ctrlKey; - keyboardEvent.shiftKey = event.shiftKey; - fn(keyboardEvent); - } + fn(event); } } private function containsUnsolicitedMemberAt(x:Float, y:Float, state:FlxTypedGroup):Bool { - if (state == null) { + if (state == null || !state.exists) { return false; } @@ -470,18 +397,23 @@ class ScreenImpl extends ScreenBase { return false; } + @:allow(haxe.ui.backend.flixel.MouseHelper) private function checkResetCursor(x:Null = null, y:Null = null) { if (x == null) { - x = MouseHelper.currentMouseX; + x = MouseHelper.currentWorldX; } if (y == null) { - y = MouseHelper.currentMouseY; + y = MouseHelper.currentWorldY; } var components = Screen.instance.findComponentsUnderPoint(x, y); + components.reverse(); var desiredCursor = "default"; var desiredCursorOffsetX:Null = null; var desiredCursorOffsetY:Null = null; for (c in components) { + if (c.style == null) { + c.validateNow(); + } if (c.style.cursor != null) { desiredCursor = c.style.cursor; desiredCursorOffsetX = c.style.cursorOffsetX; @@ -492,4 +424,4 @@ class ScreenImpl extends ScreenBase { setCursor(desiredCursor, desiredCursorOffsetX, desiredCursorOffsetY); } -} \ No newline at end of file +} diff --git a/haxe/ui/backend/TextDisplayImpl.hx b/haxe/ui/backend/TextDisplayImpl.hx index 958a310..6e7e700 100644 --- a/haxe/ui/backend/TextDisplayImpl.hx +++ b/haxe/ui/backend/TextDisplayImpl.hx @@ -12,8 +12,9 @@ class TextDisplayImpl extends TextBase { public function new() { super(); tf = new FlxText(); - tf.pixelPerfectPosition = true; + tf.pixelPerfectRender = true; tf.autoSize = true; + tf.active = false; } private override function validateData() { @@ -82,10 +83,12 @@ class TextDisplayImpl extends TextBase { private override function validateDisplay() { if (tf.textField.width != _width) { - tf.textField.width = _width * Toolkit.scaleX; + var width = _width * Toolkit.scaleX; + tf.textField.width = (width >= 1 ? width : 1); } if (tf.textField.height != _height) { - tf.textField.height = _height * Toolkit.scaleY; + var height = _height * Toolkit.scaleY; + tf.textField.height = (height >= 1 ? height : 1); } } diff --git a/haxe/ui/backend/TextInputImpl.hx b/haxe/ui/backend/TextInputImpl.hx index 061bf09..29432db 100644 --- a/haxe/ui/backend/TextInputImpl.hx +++ b/haxe/ui/backend/TextInputImpl.hx @@ -1,6 +1,9 @@ package haxe.ui.backend; -import haxe.ui.backend.flixel.textinputs.OpenFLTextInput; +typedef TextInputEvent = {type:String, stageX:Float, stageY:Float}; -class TextInputImpl extends OpenFLTextInput { -} +#if (flixel >= "5.9.0") +typedef TextInputImpl = haxe.ui.backend.flixel.textinputs.FlxTextInput; +#else +typedef TextInputImpl = haxe.ui.backend.flixel.textinputs.OpenFLTextInput; +#end diff --git a/haxe/ui/backend/TimerImpl.hx b/haxe/ui/backend/TimerImpl.hx index d69c1b2..27f0165 100644 --- a/haxe/ui/backend/TimerImpl.hx +++ b/haxe/ui/backend/TimerImpl.hx @@ -42,7 +42,7 @@ class TimerImpl { _start = Timer.stamp() + (delay / 1000); __timers.push(this); if (__timers.length == 1) { - Lib.current.stage.addEventListener(Event.ENTER_FRAME, update, false, 10000, true); + Lib.current.stage.addEventListener(Event.ENTER_FRAME, update); } } diff --git a/haxe/ui/backend/flixel/CursorHelper.hx b/haxe/ui/backend/flixel/CursorHelper.hx index c98a026..177dc10 100644 --- a/haxe/ui/backend/flixel/CursorHelper.hx +++ b/haxe/ui/backend/flixel/CursorHelper.hx @@ -1,9 +1,19 @@ package haxe.ui.backend.flixel; +import openfl.display.BitmapData; + class CursorHelper { public static var useCustomCursors:Bool = true; public static var registeredCursors:Map = new Map(); + public static var mouseLoadFunction(default, set):String->BitmapData = (bmd) -> openfl.Assets.getBitmapData(bmd); + static function set_mouseLoadFunction(v:String->BitmapData):String->BitmapData { + if (v == null) + return mouseLoadFunction; + + return mouseLoadFunction = v; + } + public static function registerCursor(name:String, graphic:String, scale:Float = 1, offsetX:Int = 0, offsetY:Int = 0) { registeredCursors.set(name, { name: name, @@ -46,4 +56,4 @@ private typedef CursorInfo = { var scale:Float; var offsetX:Int; var offsetY:Int; -} \ No newline at end of file +} diff --git a/haxe/ui/backend/flixel/FlxStyleHelper.hx b/haxe/ui/backend/flixel/FlxStyleHelper.hx index 07b329d..45020e3 100644 --- a/haxe/ui/backend/flixel/FlxStyleHelper.hx +++ b/haxe/ui/backend/flixel/FlxStyleHelper.hx @@ -15,9 +15,9 @@ import openfl.geom.Point; import openfl.geom.Rectangle; class FlxStyleHelper { - public static function applyStyle(sprite:FlxSprite, style:Style) { + public static function applyStyle(sprite:FlxSprite, style:Style):Bool { if (sprite == null || sprite.pixels == null) { - return; + return false; } var pixels:BitmapData = sprite.pixels; @@ -28,7 +28,7 @@ class FlxStyleHelper { var height:Float = sprite.frameHeight; if (width <= 0 || height <= 0) { - return; + return false; } var rc:Rectangle = new Rectangle(top, left, width, height); @@ -49,12 +49,14 @@ class FlxStyleHelper { if (useOpenFLDrawing) { var g = FlxSpriteUtil.flashGfx; - OpenFLStyleHelper.paintStyleSection(g, style, width, height, left, top); + var painted = OpenFLStyleHelper.paintStyleSection(g, style, width, height, left, top); FlxSpriteUtil.updateSpriteGraphic(sprite); - return; + return painted; } #end + var painted = false; + if (style.borderLeftSize != null && style.borderLeftSize != 0 && style.borderLeftSize == style.borderRightSize && style.borderLeftSize == style.borderBottomSize @@ -73,6 +75,8 @@ class FlxStyleHelper { pixels.fillRect(new Rectangle(rc.left, rc.height - borderSize, rc.width, borderSize), color); // bottom pixels.fillRect(new Rectangle(rc.left, rc.top + borderSize, borderSize, rc.height - (borderSize * 2)), color); // left rc.inflate(-borderSize, -borderSize); + + painted = true; } else { // compound border var org = rc.clone(); @@ -95,6 +99,10 @@ class FlxStyleHelper { var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.borderTopColor; pixels.fillRect(new Rectangle(rc.left + borderLeftSize, rc.top, org.width - (borderLeftSize + borderRightSize), borderSize), color); // top rc.top += borderSize; + + if (opacity > 0) { + painted = true; + } } if (style.borderBottomSize != null && style.borderBottomSize > 0) { @@ -103,6 +111,10 @@ class FlxStyleHelper { var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.borderBottomColor; pixels.fillRect(new Rectangle(rc.left, org.height - borderSize, rc.width, borderSize), color); // bottom rc.bottom -= borderSize; + + if (opacity > 0) { + painted = true; + } } if (style.borderLeftSize != null && style.borderLeftSize > 0) { @@ -111,6 +123,10 @@ class FlxStyleHelper { var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.borderLeftColor; pixels.fillRect(new Rectangle(rc.left, rc.top - borderTopSize, borderSize, org.height - rc.top + borderTopSize), color); // left rc.left += borderSize; + + if (opacity > 0) { + painted = true; + } } if (style.borderRightSize != null && style.borderRightSize > 0) { @@ -119,6 +135,10 @@ class FlxStyleHelper { var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.borderRightColor; pixels.fillRect(new Rectangle(org.width - borderSize, rc.top - borderTopSize, borderSize, org.height + borderTopSize), color); // right rc.right -= borderSize; + + if (opacity > 0) { + painted = true; + } } } @@ -141,6 +161,10 @@ class FlxStyleHelper { pixels.fillRect(rcLine, Std.int(opacity * 0xFF) << 24 | c); n++; } + + if (opacity > 0 && n > 0) { + painted = true; + } } else if (gradientType == "horizontal") { arr = ColorUtil.buildColorArray(style.backgroundColor, style.backgroundColorEnd, Std.int(rc.width)); for (c in arr) { @@ -148,20 +172,31 @@ class FlxStyleHelper { pixels.fillRect(rcLine, Std.int(opacity * 0xFF) << 24 | c); n++; } + + if (opacity > 0 && n > 0) { + painted = true; + } } } else { var color:FlxColor = Std.int(opacity * 0xFF) << 24 | style.backgroundColor; pixels.fillRect(rc, color); + + if (opacity > 0) { + painted = true; + } } } if (style.backgroundImage != null) { + painted = true; Toolkit.assets.getImage(style.backgroundImage, function(info:ImageInfo) { if (info != null && info.data != null) { paintBackroundImage(sprite, info.data, style); } }); } + + return painted; } private static function paintBackroundImage(sprite:FlxSprite, data:ImageData, style:Style) { diff --git a/haxe/ui/backend/flixel/KeyboardHelper.hx b/haxe/ui/backend/flixel/KeyboardHelper.hx new file mode 100644 index 0000000..889b220 --- /dev/null +++ b/haxe/ui/backend/flixel/KeyboardHelper.hx @@ -0,0 +1,132 @@ +package haxe.ui.backend.flixel; + +import flixel.FlxG; +import haxe.ui.core.Component; +import haxe.ui.events.KeyboardEvent; +import haxe.ui.focus.FocusManager; + +@:structInit +class KeyboardCallback { + public var fn:KeyboardEvent->Void; + public var priority:Int; +} + +class KeyboardHelper { + private static var _initialized = false; + private static var _callbacks:Map> = new Map>(); + + public static function init() { + if (_initialized == true) { + return; + } + + _initialized = true; + + FlxG.stage.addEventListener(openfl.events.KeyboardEvent.KEY_DOWN, onKeyDown); + FlxG.stage.addEventListener(openfl.events.KeyboardEvent.KEY_UP, onKeyUp); + } + + public static function notify(event:String, callback:KeyboardEvent->Void, priority:Int = 5) { + var list = _callbacks.get(event); + if (list == null) { + list = new Array(); + _callbacks.set(event, list); + } + + if (!hasCallback(list, callback)) { + list.insert(0, { + fn: callback, + priority: priority + }); + + list.sort(function(a, b) { + return a.priority - b.priority; + }); + } + } + + public static function remove(event:String, callback:KeyboardEvent->Void) { + var list = _callbacks.get(event); + if (list != null) { + removeCallback(list, callback); + if (list.length == 0) { + _callbacks.remove(event); + } + } + } + + private static function onKeyDown(e:openfl.events.KeyboardEvent) { + dispatchEvent(KeyboardEvent.KEY_DOWN, e); + } + + private static function onKeyUp(e:openfl.events.KeyboardEvent) { + dispatchEvent(KeyboardEvent.KEY_UP, e); + } + + private static function dispatchEvent(type:String, e:openfl.events.KeyboardEvent) { + var event = new KeyboardEvent(type); + event.keyCode = e.keyCode; + event.altKey = e.altKey; + event.ctrlKey = e.ctrlKey; + event.shiftKey = e.shiftKey; + + var target = getTarget(); + // recreate a bubbling effect, so components will pass events onto their parents + // can't use the `bubble` property as it causes a crash when `target` isn't the expected result, for example, on ListView.onRendererClick + while (target != null) { + if (target.hasEvent(event.type)) { + target.dispatch(event); + if (event.canceled == true) { + return; + } + } + target = target.parentComponent; + } + + var list = _callbacks.get(type); + if (list == null || list.length == 0) { + return; + } + + list = list.copy(); + + for (l in list) { + l.fn(event); + if (event.canceled == true) { + break; + } + } + } + + private static function getTarget():Component { + var target:Component = cast FocusManager.instance.focus; + if (target != null && target.state == StateHelper.currentState) { + return target; + } + return null; + } + + private static function hasCallback(list:Array, fn:KeyboardEvent->Void):Bool { + var has = false; + for (item in list) { + if (item.fn == fn) { + has = true; + break; + } + } + return has; + } + + private static function removeCallback(list:Array, fn:KeyboardEvent->Void) { + var itemToRemove:KeyboardCallback = null; + for (item in list) { + if (item.fn == fn) { + itemToRemove = item; + break; + } + } + if (itemToRemove != null) { + list.remove(itemToRemove); + } + } +} \ No newline at end of file diff --git a/haxe/ui/backend/flixel/MouseHelper.hx b/haxe/ui/backend/flixel/MouseHelper.hx index 84c0e56..ea17f1d 100644 --- a/haxe/ui/backend/flixel/MouseHelper.hx +++ b/haxe/ui/backend/flixel/MouseHelper.hx @@ -1,63 +1,59 @@ package haxe.ui.backend.flixel; import flixel.FlxG; +import haxe.ui.core.Component; +import haxe.ui.core.Platform; +import haxe.ui.core.Screen; import haxe.ui.events.MouseEvent; -typedef Callback = { - var fn:MouseEvent->Void; - var priority:Int; +@:structInit +class MouseCallback { + public var fn:MouseEvent->Void; + public var priority:Int; } class MouseHelper { public static var currentMouseX:Float = 0; public static var currentMouseY:Float = 0; + public static var currentWorldX:Float = 0; + public static var currentWorldY:Float = 0; - private static var _hasOnMouseDown:Bool = false; - private static var _hasOnMouseUp:Bool = false; - private static var _hasOnMouseMove:Bool = false; - private static var _hasOnMouseWheel:Bool = false; - - private static var _callbacks:Map> = new Map>(); - - private static var _inputManager:InputManager = null; - - public static function notify(event:String, callback:MouseEvent->Void, priority:Int = 5) { - switch (event) { - case MouseEvent.MOUSE_DOWN: - if (_hasOnMouseDown == false) { - FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_DOWN, onMouseDown); - FlxG.stage.addEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_DOWN, onMouseDown); - + private static var _initialized = false; + private static var _callbacks:Map> = new Map>(); - FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_DOWN, onStageDown); - FlxG.stage.addEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_DOWN, onStageRightDown); - _hasOnMouseDown = true; - } - case MouseEvent.MOUSE_UP: - if (_hasOnMouseUp == false) { - FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_UP, onMouseUp); - FlxG.stage.addEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_UP, onMouseUp); + private static var _mouseDownLeft:Dynamic; + private static var _mouseDownMiddle:Dynamic; + private static var _mouseDownRight:Dynamic; + private static var _lastClickTarget:Dynamic; + private static var _lastClickTime:Float; + private static var _mouseOverTarget:Dynamic; - FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_UP, onStageUp); - FlxG.stage.addEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_UP, onStageRightUp); - _hasOnMouseUp = true; - FlxG.signals.preStateSwitch.add(onPreStateSwitched); - } - case MouseEvent.MOUSE_MOVE: - if (_hasOnMouseMove == false) { - FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_MOVE, onMouseMove); - _hasOnMouseMove = true; - } - case MouseEvent.MOUSE_WHEEL: - if (_hasOnMouseWheel == false) { - FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_WHEEL, onMouseWheel); - _hasOnMouseWheel = true; - } + public static function init() { + if (_initialized == true) { + return; } + _initialized = true; + + FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_DOWN, onMouseDown); + FlxG.stage.addEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_DOWN, onMouseDown); + FlxG.stage.addEventListener(openfl.events.MouseEvent.MIDDLE_MOUSE_DOWN, onMouseDown); + + FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_UP, onMouseUp); + FlxG.stage.addEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_UP, onMouseUp); + FlxG.stage.addEventListener(openfl.events.MouseEvent.MIDDLE_MOUSE_UP, onMouseUp); + + FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_MOVE, onMouseMove); + + FlxG.stage.addEventListener(openfl.events.MouseEvent.MOUSE_WHEEL, onMouseWheel); + + FlxG.signals.preStateSwitch.add(onPreStateSwitched); + } + + public static function notify(event:String, callback:MouseEvent->Void, priority:Int = 5) { var list = _callbacks.get(event); if (list == null) { - list = new Array(); + list = new Array(); _callbacks.set(event, list); } @@ -66,17 +62,11 @@ class MouseHelper { fn: callback, priority: priority }); - + list.sort(function(a, b) { return a.priority - b.priority; }); } - - if (_inputManager == null) { - _inputManager = new InputManager(); - _inputManager.onResetCb = onPreStateSwitched; - FlxG.inputs.add(_inputManager); - } } public static function remove(event:String, callback:MouseEvent->Void) { @@ -85,146 +75,145 @@ class MouseHelper { removeCallback(list, callback); if (list.length == 0) { _callbacks.remove(event); - - switch (event) { - case MouseEvent.MOUSE_DOWN: - if (_hasOnMouseDown == true) { - FlxG.stage.removeEventListener(openfl.events.MouseEvent.MOUSE_DOWN, onMouseDown); - FlxG.stage.removeEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_DOWN, onMouseDown); - - FlxG.stage.removeEventListener(openfl.events.MouseEvent.MOUSE_DOWN, onStageDown); - FlxG.stage.removeEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_DOWN, onStageRightDown); - _hasOnMouseDown = false; - } - case MouseEvent.MOUSE_UP: - if (_hasOnMouseUp == true) { - FlxG.stage.removeEventListener(openfl.events.MouseEvent.MOUSE_UP, onMouseUp); - FlxG.stage.removeEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_UP, onMouseUp); - - FlxG.stage.removeEventListener(openfl.events.MouseEvent.MOUSE_UP, onStageUp); - FlxG.stage.removeEventListener(openfl.events.MouseEvent.RIGHT_MOUSE_UP, onStageRightUp); - _hasOnMouseUp = false; - FlxG.signals.preStateSwitch.remove(onPreStateSwitched); - } - case MouseEvent.MOUSE_MOVE: - if (_hasOnMouseMove == true) { - FlxG.stage.removeEventListener(openfl.events.MouseEvent.MOUSE_MOVE, onMouseMove); - _hasOnMouseMove = false; - } - case MouseEvent.MOUSE_WHEEL: - if (_hasOnMouseWheel == true) { - FlxG.stage.removeEventListener(openfl.events.MouseEvent.MOUSE_WHEEL, onMouseWheel); - _hasOnMouseWheel = false; - } - } } } } private static function onPreStateSwitched() { - onMouseUp(null); - onMouseMove(null); + // simulate mouse events when states switch to mop up any visual styles + onMouse(MouseEvent.MOUSE_UP, currentMouseX, currentMouseY); + onMouse(MouseEvent.MOUSE_MOVE, currentMouseX, currentMouseY); } private static function onMouseDown(e:openfl.events.MouseEvent) { - if (e != null) { - currentMouseX = e.stageX; - currentMouseY = e.stageY; - } - - var list = _callbacks.get(MouseEvent.MOUSE_DOWN); - if (list == null || list.length == 0) { - return; - } - - list = list.copy(); - - var event = new MouseEvent(MouseEvent.MOUSE_DOWN); - if (e != null) { - event.screenX = (e.stageX - FlxG.scaleMode.offset.x) / (FlxG.scaleMode.scale.x * initialZoom()); - event.screenY = (e.stageY - FlxG.scaleMode.offset.y) / (FlxG.scaleMode.scale.y * initialZoom()); - } else { - event.screenX = (currentMouseX - FlxG.scaleMode.offset.x) / (FlxG.scaleMode.scale.x * initialZoom()); - event.screenY = (currentMouseY - FlxG.scaleMode.offset.y) / (FlxG.scaleMode.scale.y * initialZoom()); - } - event.data = buttonPressed; - for (l in list) { - l.fn(event); - if (event.canceled == true) { - break; - } + var type = switch (e.type) { + case openfl.events.MouseEvent.MIDDLE_MOUSE_DOWN: MouseEvent.MIDDLE_MOUSE_DOWN; + case openfl.events.MouseEvent.RIGHT_MOUSE_DOWN: MouseEvent.RIGHT_MOUSE_DOWN; + default: MouseEvent.MOUSE_DOWN; } + onMouse(type, e.stageX, e.stageY, e.buttonDown, e.ctrlKey, e.shiftKey); } private static function onMouseUp(e:openfl.events.MouseEvent) { - if (e != null) { - currentMouseX = e.stageX; - currentMouseY = e.stageY; - } - - var list = _callbacks.get(MouseEvent.MOUSE_UP); - if (list == null || list.length == 0) { - return; - } - - list = list.copy(); - - var event = new MouseEvent(MouseEvent.MOUSE_UP); - if (e != null) { - event.screenX = (e.stageX - FlxG.scaleMode.offset.x) / (FlxG.scaleMode.scale.x * initialZoom()); - event.screenY = (e.stageY - FlxG.scaleMode.offset.y) / (FlxG.scaleMode.scale.y * initialZoom()); - } else { - event.screenX = (currentMouseX - FlxG.scaleMode.offset.x) / (FlxG.scaleMode.scale.x * initialZoom()); - event.screenY = (currentMouseY - FlxG.scaleMode.offset.y) / (FlxG.scaleMode.scale.y * initialZoom()); - } - event.data = buttonPressed; - for (l in list) { - l.fn(event); - if (event.canceled == true) { - break; - } + var type = switch (e.type) { + case openfl.events.MouseEvent.MIDDLE_MOUSE_UP: MouseEvent.MIDDLE_MOUSE_UP; + case openfl.events.MouseEvent.RIGHT_MOUSE_UP: MouseEvent.RIGHT_MOUSE_UP; + default: MouseEvent.MOUSE_UP; } + onMouse(type, e.stageX, e.stageY, e.buttonDown, e.ctrlKey, e.shiftKey); } private static function onMouseMove(e:openfl.events.MouseEvent) { - if (e != null) { - currentMouseX = e.stageX; - currentMouseY = e.stageY; + onMouse(MouseEvent.MOUSE_MOVE, e.stageX, e.stageY, e.buttonDown, e.ctrlKey, e.shiftKey); + } + + private static function onMouseWheel(e:openfl.events.MouseEvent) { + var event = createEvent(MouseEvent.MOUSE_WHEEL, e.buttonDown, e.ctrlKey, e.shiftKey); + event.delta = Math.max(-1, Math.min(1, e.delta)); + + var target:Dynamic = getTarget(currentWorldX, currentWorldY); + + dispatchEvent(event, target); + } + + private static function onMouse(type:String, x:Float, y:Float, buttonDown:Bool = false, ctrlKey:Bool = false, shiftKey:Bool = false) { + if (currentMouseX != x) { + currentMouseX = x; + currentWorldX = ((currentMouseX - FlxG.scaleMode.offset.x) / (FlxG.scaleMode.scale.x * initialZoom())) / Toolkit.scaleX; } - - var list = _callbacks.get(MouseEvent.MOUSE_MOVE); - if (list == null || list.length == 0) { - return; + if (currentMouseY != y) { + currentMouseY = y; + currentWorldY = ((currentMouseY - FlxG.scaleMode.offset.y) / (FlxG.scaleMode.scale.y * initialZoom())) / Toolkit.scaleY; } - - list = list.copy(); - var event = new MouseEvent(MouseEvent.MOUSE_MOVE); - if (e != null) { - event.screenX = (e.stageX - FlxG.scaleMode.offset.x) / (FlxG.scaleMode.scale.x * initialZoom()); - event.screenY = (e.stageY - FlxG.scaleMode.offset.y) / (FlxG.scaleMode.scale.y * initialZoom()); - } else { - event.screenX = (currentMouseX - FlxG.scaleMode.offset.x) / (FlxG.scaleMode.scale.x * initialZoom()); - event.screenY = (currentMouseY - FlxG.scaleMode.offset.y) / (FlxG.scaleMode.scale.y * initialZoom()); + + var target:Dynamic = getTarget(currentWorldX, currentWorldY); + + if (target != _mouseOverTarget) { + Screen.instance.checkResetCursor(); + if (_mouseOverTarget != null) { + dispatchEventType(MouseEvent.MOUSE_OUT, buttonDown, ctrlKey, shiftKey, _mouseOverTarget); + } + dispatchEventType(MouseEvent.MOUSE_OVER, buttonDown, ctrlKey, shiftKey, target); + _mouseOverTarget = target; } - for (l in list) { - l.fn(event); - if (event.canceled == true) { - break; + var clickType:String = null; + switch (type) { + case MouseEvent.MOUSE_DOWN: + _mouseDownLeft = target; + + case MouseEvent.MIDDLE_MOUSE_DOWN: + _mouseDownMiddle = target; + + case MouseEvent.RIGHT_MOUSE_DOWN: + _mouseDownRight = target; + + case MouseEvent.MOUSE_UP: + if (_mouseDownLeft == target) { + clickType = MouseEvent.CLICK; + } + _mouseDownLeft = null; + + case MouseEvent.MIDDLE_MOUSE_UP: + if (_mouseDownMiddle == target) { + clickType = MouseEvent.MIDDLE_CLICK; + } + _mouseDownMiddle = null; + + case MouseEvent.RIGHT_MOUSE_UP: + if (_mouseDownRight == target) { + clickType = MouseEvent.RIGHT_CLICK; + } + _mouseDownRight = null; + } + + dispatchEventType(type, buttonDown, ctrlKey, shiftKey, target); + + if (clickType != null) { + dispatchEventType(clickType, buttonDown, ctrlKey, shiftKey, target); + + if (type == MouseEvent.MOUSE_UP) { + var currentTime = Timer.stamp(); + if (currentTime - _lastClickTime < 0.5 && target == _lastClickTarget) { + dispatchEventType(MouseEvent.DBL_CLICK, buttonDown, ctrlKey, shiftKey, target); + _lastClickTime = 0; + _lastClickTarget = null; + } else { + _lastClickTarget = target; + _lastClickTime = currentTime; + } } } } - - private static function onMouseWheel(e:openfl.events.MouseEvent) { - var list = _callbacks.get(MouseEvent.MOUSE_WHEEL); + + private static function dispatchEventType(type:String, buttonDown:Bool, ctrlKey:Bool, shiftKey:Bool, target:Dynamic) { + var event = createEvent(type, buttonDown, ctrlKey, shiftKey); + dispatchEvent(event, target); + } + + private static function dispatchEvent(event:MouseEvent, target:Dynamic) { + if ((target is Component)) { + var c:Component = cast target; + // recreate a bubbling effect, so components will pass events onto their parents + // can't use the `bubble` property as it causes a crash when `target` isn't the expected result, for example, on ListView.onRendererClick + while (c != null) { + if (c.hasEvent(event.type)) { + c.dispatch(event); + if (event.canceled == true) { + return; + } + } + c = c.parentComponent; + } + } + + var list = _callbacks.get(event.type); if (list == null || list.length == 0) { return; } list = list.copy(); - - var event = new MouseEvent(MouseEvent.MOUSE_WHEEL); - event.delta = -e.delta; + for (l in list) { l.fn(event); if (event.canceled == true) { @@ -232,43 +221,27 @@ class MouseHelper { } } } - - private static function onStageDown(_) { - _buttonPressed = 0; - } - - private static function onStageUp(_) { - _buttonPressed = -1; - } - private static function onStageRightDown(_) { - _buttonPressed = 1; + private static function createEvent(type:String, buttonDown:Bool, ctrlKey:Bool, shiftKey:Bool):MouseEvent { + var event = new MouseEvent(type); + event.screenX = currentWorldX; + event.screenY = currentWorldY; + event.buttonDown = buttonDown; + event.touchEvent = Platform.instance.isMobile; + event.ctrlKey = ctrlKey; + event.shiftKey = shiftKey; + return event; } - private static function onStageRightUp(_) { - _buttonPressed = -1; - } - - private static var _buttonPressed:Int = -1; - private static var buttonPressed(get, null):Int; - private static function get_buttonPressed():Int { - var n = _buttonPressed; - - #if FLX_NO_MOUSE - - n = 0; - - #else - - if (FlxG.mouse.pressed == true) { - n = 0; - } else if (FlxG.mouse.pressedRight == true) { - n = 1; + private static function getTarget(x:Float, y:Float):Dynamic { + var components = Screen.instance.findComponentsUnderPoint(x, y); + components.reverse(); + for (c in components) { + if (c.state == StateHelper.currentState) { + return c; + } } - - #end - - return n; + return Screen.instance; } private static inline function initialZoom():Float { @@ -283,7 +256,7 @@ class MouseHelper { #end } - private static function hasCallback(list:Array, fn:MouseEvent->Void):Bool { + private static function hasCallback(list:Array, fn:MouseEvent->Void):Bool { var has = false; for (item in list) { if (item.fn == fn) { @@ -294,8 +267,8 @@ class MouseHelper { return has; } - private static function removeCallback(list:Array, fn:MouseEvent->Void) { - var itemToRemove:Callback = null; + private static function removeCallback(list:Array, fn:MouseEvent->Void) { + var itemToRemove:MouseCallback = null; for (item in list) { if (item.fn == fn) { itemToRemove = item; diff --git a/haxe/ui/backend/flixel/OpenFLStyleHelper.hx b/haxe/ui/backend/flixel/OpenFLStyleHelper.hx index fcb3665..338dea8 100644 --- a/haxe/ui/backend/flixel/OpenFLStyleHelper.hx +++ b/haxe/ui/backend/flixel/OpenFLStyleHelper.hx @@ -12,13 +12,13 @@ class OpenFLStyleHelper { public function new() { } - public static function paintStyleSection(graphics:Graphics, style:Style, width:Float, height:Float, left:Float = 0, top:Float = 0, clear:Bool = true) { + public static function paintStyleSection(graphics:Graphics, style:Style, width:Float, height:Float, left:Float = 0, top:Float = 0, clear:Bool = true):Bool { if (clear == true) { graphics.clear(); } if (width <= 0 || height <= 0) { - return; + return false; } /* @@ -45,6 +45,8 @@ class OpenFLStyleHelper { borderRadius = style.borderRadius * Toolkit.scale; } + var painted = false; + if (style.borderLeftSize != null && style.borderLeftSize != 0 && style.borderLeftSize == style.borderRightSize && style.borderLeftSize == style.borderBottomSize @@ -61,6 +63,8 @@ class OpenFLStyleHelper { rc.bottom -= (style.borderLeftSize * Toolkit.scale) / 2; rc.right -= (style.borderLeftSize * Toolkit.scale) / 2; //rc.inflate( -(style.borderLeftSize / 2), -(style.borderLeftSize / 2)); + + painted = true; } else { // compound border if ((style.borderTopSize != null && style.borderTopSize > 0) || (style.borderBottomSize != null && style.borderBottomSize > 0) @@ -100,6 +104,8 @@ class OpenFLStyleHelper { rc.right -= (style.borderRightSize * Toolkit.scale); } + + painted = true; } } @@ -147,6 +153,10 @@ class OpenFLStyleHelper { } else { graphics.beginFill(backgroundColor, backgroundOpacity); } + + if (backgroundOpacity > 0) { + painted = true; + } } if (borderRadius == 0) { @@ -165,5 +175,7 @@ class OpenFLStyleHelper { } graphics.endFill(); + + return painted; } } \ No newline at end of file diff --git a/haxe/ui/backend/flixel/StateHelper.hx b/haxe/ui/backend/flixel/StateHelper.hx index b0c43f3..a38ad52 100644 --- a/haxe/ui/backend/flixel/StateHelper.hx +++ b/haxe/ui/backend/flixel/StateHelper.hx @@ -32,7 +32,7 @@ class StateHelper { group = currentState; } - if (group == null || group.members == null) { + if (group == null || !group.exists) { return false; } @@ -60,7 +60,7 @@ class StateHelper { } private static function groupHasMember(member:FlxBasic, group:FlxSpriteGroup) { - if (group == null || group.group == null || group.members == null) { + if (group == null || !group.exists) { return false; } @@ -92,7 +92,7 @@ class StateHelper { group = currentState; } - if (group == null || group.members == null) { + if (group == null || !group.exists) { return null; } diff --git a/haxe/ui/backend/flixel/UIFragment.hx b/haxe/ui/backend/flixel/UIFragment.hx new file mode 100644 index 0000000..1dd63b7 --- /dev/null +++ b/haxe/ui/backend/flixel/UIFragment.hx @@ -0,0 +1,5 @@ +package haxe.ui.backend.flixel; + +class UIFragment extends UIFragmentBase { + +} \ No newline at end of file diff --git a/haxe/ui/backend/flixel/UIFragmentBase.hx b/haxe/ui/backend/flixel/UIFragmentBase.hx new file mode 100644 index 0000000..0e6aed4 --- /dev/null +++ b/haxe/ui/backend/flixel/UIFragmentBase.hx @@ -0,0 +1,3 @@ +package haxe.ui.backend.flixel; + +typedef UIFragmentBase = flixel.group.FlxSpriteContainer; \ No newline at end of file diff --git a/haxe/ui/backend/flixel/UIRTTITools.hx b/haxe/ui/backend/flixel/UIRTTITools.hx new file mode 100644 index 0000000..f8fc6c8 --- /dev/null +++ b/haxe/ui/backend/flixel/UIRTTITools.hx @@ -0,0 +1,252 @@ +package haxe.ui.backend.flixel; + +import haxe.ui.RuntimeComponentBuilder; +import haxe.ui.core.Component; +import haxe.rtti.CType; +import haxe.ui.core.ComponentClassMap; + +using StringTools; + +class UIRTTITools { + public static function buildViaRTTI(rtti:Classdef):Component { + var root:Component = null; + var m = getMetaWithValueRTTI(rtti.meta, "build", "haxe.ui.RuntimeComponentBuilder.build"); + if (m != null) { + var assetId = m.params[0].replace("haxe.ui.RuntimeComponentBuilder.build(", "").replace(")", ""); + assetId = assetId.replace("\"", ""); + assetId = assetId.replace("'", ""); + root = RuntimeComponentBuilder.fromAsset(assetId); + if (root == null) { + throw "could not loading runtime ui from asset (" + assetId + ")"; + } + } + m = getMetaRTTI(rtti.meta, "xml"); + if (m != null) { // comes back as an escaped CDATA section + var xmlString = m.params[0].trim(); + if (xmlString.startsWith("")) { + xmlString = xmlString.substring(0, xmlString.length - "]]>".length); + } + if (xmlString.startsWith("\"")) { + xmlString = xmlString.substring(1); + } + if (xmlString.endsWith("\"")) { + xmlString = xmlString.substring(0, xmlString.length - 1); + } + xmlString = xmlString.replace("\\r", "\r"); + xmlString = xmlString.replace("\\n", "\n"); + xmlString = xmlString.replace("\\\"", "\""); + xmlString = xmlString.trim(); + try { + root = RuntimeComponentBuilder.fromString(xmlString); + } catch (e:Dynamic) { + trace("ERROR", e); + trace(haxe.CallStack.toString(haxe.CallStack.exceptionStack(true))); + } + } + return root; + } + + public static function linkViaRTTI(rtti:Classdef, target:Dynamic, root:Component, force:Bool = false) { + if (root == null) { + return; + } + for (f in rtti.fields) { + switch (f.type) { + case CClass(name, params): + if (ComponentClassMap.instance.hasClassName(name)) { + var candidate = root.findComponent(f.name); + if (force) { + Reflect.setField(target, f.name, null); + } + if (candidate != null && Reflect.field(target, f.name) == null) { + var temp = Type.createEmptyInstance(Type.resolveClass(name)); + if ((temp is IComponentDelegate)) { + var componentDelegate:IComponentDelegate = Type.createEmptyInstance(Type.resolveClass(name)); + componentDelegate.component = candidate; + Reflect.setField(target, f.name, componentDelegate); + } else { + Reflect.setField(target, f.name, candidate); + } + } + } + case CFunction(args, ret): + var m = getMetaRTTI(f.meta, "bind"); + if (m != null) { + if (m.params[0] == "this") { + if ((target is IComponentDelegate)) { + var componentDelegate:IComponentDelegate = cast target; + bindEvent(rtti, componentDelegate.component, f.name, target, m.params[1], true); + } else { + bindEvent(rtti, root, f.name, target, m.params[1]); + } + } else { + var candidate:Component = root.findComponent(m.params[0]); + if (candidate != null) { + bindEvent(rtti, candidate, f.name, target, m.params[1]); + } else { + // another perfectly valid contruct, albeit less common (though still useful), is the ability to use + // @:bind to bind to variables on static fields, eg: + // @:bind(MyClass.instance, SomeEvent.EventType) + // this code facilitates that by attempting to resolve the item and binding the event to it + var parts = m.params[0].split("."); + var className = parts.shift(); + var c = Type.resolveClass(className); + + if (c == null) { + // this allows full qualified class names: + // @:bind(some.pkg.MyClass.instance, SomeEvent.EventType) + // by looping over each part looking for a valid class + // its not fast (or pretty), but it will only happen once (per @:bind) + var candidateClass = className; + while (parts.length > 0) { + var part = parts.shift(); + candidateClass += "." + part; + var temp = Type.resolveClass(candidateClass); + if (temp != null) { + c = temp; + break; + } + } + } + + if (c != null) { + var ref:Dynamic = c; + var found = (parts.length > 0); + for (part in parts) { + if (!Reflect.hasField(ref, part)) { + found = false; + break; + } + ref = Reflect.field(ref, part); + } + if (found) { + if (ref != null) { + if ((ref is UIRuntimeState)) { + var state:UIRuntimeState = cast ref; + bindEvent(rtti, state.root, f.name, target, m.params[1]); + } else if ((ref is UIRuntimeSubState)) { + var subState:UIRuntimeSubState = cast ref; + bindEvent(rtti, subState.root, f.name, target, m.params[1]); + } else if ((ref is IComponentDelegate)) { + var componentDelegate:IComponentDelegate = cast ref; + bindEvent(rtti, componentDelegate.component, f.name, target, m.params[1]); + } else if ((ref is Component)) { + var component:Component = cast ref; + bindEvent(rtti, component, f.name, target, m.params[1]); + } + } else { + throw "bind param resolved, but was null '" + m.params[0] + "'"; + } + } else { + throw "could not resolve bind param '" + m.params[0] + "'"; + } + } else { + throw "could not resolve class '" + className + "'"; + } + } + } + } + case _: + } + } + } + + private static function bindEvent(rtti:Classdef, candidate:Component, fieldName:String, target:Dynamic, eventClass:String, isComponentDelegate:Bool = false) { + if (candidate == null) { + return; + } + var parts = eventClass.split("."); + var eventName = parts.pop(); + eventClass = parts.join("."); + var c = resolveEventClass(rtti, eventClass); + if (c != null) { + var eventString = Reflect.field(c, eventName); + var fn = Reflect.field(target, fieldName); + // this may be ill-concieved, but if we are talking about a component delegate (ie, a fragment) + // it means we are going to attach a component to an "empty" class which means this code has + // already run once, meaning there are two event listeners, this way we remove them first + // in practice its probably _exactly_ what we want, but this could also clear up binding + // two functions to the same event (which isnt common at all) + if (isComponentDelegate) { + candidate.unregisterEvents(eventString); + } + candidate.registerEvent(eventString, fn); + } else { + throw "could not resolve event class '" + eventClass + "' (you may need to use fully qualified class names)"; + } + } + + private static function resolveEventClass(rtti:Classdef, eventClass:String) { + var candidateEvent = "haxe.ui.events." + eventClass; + var event = Type.resolveClass(candidateEvent); + if (event != null) { + return event; + } + + var event = Type.resolveClass(eventClass); + if (event != null) { + return event; + } + + // this is pretty brute force method, were going to see if we can find any functions + // with @:bind meta, these are presumably the event handlers, if we can find one + // where the the last part of the arg type (which would be the event type) matches + // the event we are looking for, we'll consider that match, and can use that as a + // fully qualified event class + for (f in rtti.fields) { + switch (f.type) { + case CFunction(args, ret): + if (getMetaRTTI(f.meta, "bind") != null) { + for (arg in args) { + switch (arg.t) { + case CClass(name, params): + if (name.endsWith(eventClass)) { + var event = Type.resolveClass(name); + if (event != null) { + return event; + } + } + case _: + } + } + } + case _: + } + } + + return null; + } + + private static function getMetaRTTI(metadata:MetaData, name:String):{name:String, params:Array} { + for (m in metadata) { + if (m.name == name || m.name == ":" + name) { + return m; + } + } + return null; + } + + private static function getMetasRTTI(metadata:MetaData, name:String):Array<{name:String, params:Array}> { + var metas = []; + for (m in metadata) { + if (m.name == name || m.name == ":" + name) { + metas.push(m); + } + } + return metas; + } + + private static function getMetaWithValueRTTI(metadata:MetaData, name:String, value:String, paramIndex:Int = 0):{name:String, params:Array} { + for (m in metadata) { + if (m.name == name || m.name == ":" + name) { + if (m.params[paramIndex].startsWith(value)) { + return m; + } + } + } + return null; + } +} \ No newline at end of file diff --git a/haxe/ui/backend/flixel/UIRuntimeFragment.hx b/haxe/ui/backend/flixel/UIRuntimeFragment.hx new file mode 100644 index 0000000..6e23892 --- /dev/null +++ b/haxe/ui/backend/flixel/UIRuntimeFragment.hx @@ -0,0 +1,130 @@ +package haxe.ui.backend.flixel; + +import haxe.ui.RuntimeComponentBuilder; +import haxe.ui.core.Component; +import haxe.ui.events.UIEvent; +import haxe.ui.events.EventType; +import haxe.ui.backend.flixel.UIRTTITools.*; + +using StringTools; + +@:rtti +class UIRuntimeFragment extends UIFragmentBase implements IComponentDelegate { // uses rtti to "build" a class with a similar experience to using macros + public var root:Component; + + public function new() { + super(); + + scrollFactor.set(0, 0); // ui doesn't scroll by default + + var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this)); + root = buildViaRTTI(rtti); + linkViaRTTI(rtti, this, root); + if (root != null) { + root.registerEvent(UIEvent.READY, (_) -> { + onReady(); + }); + add(root); + } + } + + private function onReady() { + } + + public var component(get, set):Component; + private function get_component():Component { + return root; + } + private function set_component(value:Component):Component { + root = value; + var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this)); + linkViaRTTI(rtti, this, root); + return value; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // util functions + ///////////////////////////////////////////////////////////////////////////////////////////////// + public function addComponent(child:Component):Component { + if (root == null) { + throw "no root component"; + } + + return root.addComponent(child); + } + + public function removeComponent(child:Component):Component { + if (root == null) { + throw "no root component"; + } + + return root.removeComponent(child); + } + + public function findComponent(criteria:String = null, type:Class = null, recursive:Null = null, searchType:String = "id"):Null { + if (root == null) { + throw "no root component"; + } + + return root.findComponent(criteria, type, recursive, searchType); + } + + public function findComponents(styleName:String = null, type:Class = null, maxDepth:Int = 5):Array { + if (root == null) { + throw "no root component"; + } + + return root.findComponents(styleName, type, maxDepth); + } + + public function findAncestor(criteria:String = null, type:Class = null, searchType:String = "id"):Null { + if (root == null) { + throw "no root component"; + } + + return root.findAncestor(criteria, type, searchType); + } + + public function findComponentsUnderPoint(screenX:Float, screenY:Float, type:Class = null):Array { + if (root == null) { + throw "no root component"; + } + + return root.findComponentsUnderPoint(screenX, screenY, type); + } + + public function dispatch(event:T) { + if (root == null) { + throw "no root component"; + } + + root.dispatch(event); + } + + public function registerEvent(type:EventType, listener:T->Void, priority:Int = 0) { + if (root == null) { + throw "no root component"; + } + + root.registerEvent(type, listener, priority); + } + + public function show() { + if (root == null) { + throw "no root component"; + } + root.show(); + } + + public function hide() { + if (root == null) { + throw "no root component"; + } + root.hide(); + } + + public override function destroy() { + super.destroy(); + root = null; + } +} diff --git a/haxe/ui/backend/flixel/UIRuntimeState.hx b/haxe/ui/backend/flixel/UIRuntimeState.hx index 69260d1..80cfdfb 100644 --- a/haxe/ui/backend/flixel/UIRuntimeState.hx +++ b/haxe/ui/backend/flixel/UIRuntimeState.hx @@ -1,10 +1,10 @@ package haxe.ui.backend.flixel; -import haxe.rtti.CType; -import haxe.ui.RuntimeComponentBuilder; import haxe.ui.core.Component; -import haxe.ui.core.ComponentClassMap; import haxe.ui.core.Screen; +import haxe.ui.events.UIEvent; +import haxe.ui.events.EventType; +import haxe.ui.backend.flixel.UIRTTITools.*; using StringTools; @@ -12,22 +12,26 @@ using StringTools; class UIRuntimeState extends UIStateBase { // uses rtti to "build" a class with a similar experience to using macros public var root:Component; - public var assetId:String; - - public function new(assetId:String = null) { + public function new() { super(); - this.assetId = assetId; } public override function create() { - buildViaRTTI(); - linkViaRTTI(); + var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this)); + root = buildViaRTTI(rtti); + linkViaRTTI(rtti, this, root); if (root != null) { + root.registerEvent(UIEvent.READY, (_) -> { + onReady(); + }); Screen.instance.addComponent(root); } super.create(); } + private function onReady() { + } + ///////////////////////////////////////////////////////////////////////////////////////////////// // util functions ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -79,117 +83,41 @@ class UIRuntimeState extends UIStateBase { // uses rtti to "build" a class with return root.findComponentsUnderPoint(screenX, screenY, type); } - ///////////////////////////////////////////////////////////////////////////////////////////////// - // rtti functions - ///////////////////////////////////////////////////////////////////////////////////////////////// - private function buildViaRTTI() { - var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this)); - var m = getMetaWithValueRTTI(rtti.meta, "build", "haxe.ui.RuntimeComponentBuilder.build"); - if (m != null) { - assetId = m.params[0].replace("haxe.ui.RuntimeComponentBuilder.build(", "").replace(")", ""); - assetId = assetId.replace("\"", ""); - assetId = assetId.replace("'", ""); - this.root = RuntimeComponentBuilder.fromAsset(assetId); - } - m = getMetaRTTI(rtti.meta, "xml"); - if (m != null) { // comes back as an escaped CDATA section - var xmlString = m.params[0].trim(); - if (xmlString.startsWith("")) { - xmlString = xmlString.substring(0, xmlString.length - "]]>".length); - } - if (xmlString.startsWith("\"")) { - xmlString = xmlString.substring(1); - } - if (xmlString.endsWith("\"")) { - xmlString = xmlString.substring(0, xmlString.length - 1); - } - xmlString = xmlString.replace("\\r", "\r"); - xmlString = xmlString.replace("\\n", "\n"); - xmlString = xmlString.replace("\\\"", "\""); - xmlString = xmlString.trim(); - try { - this.root = RuntimeComponentBuilder.fromString(xmlString); - } catch (e:Dynamic) { - trace("ERROR", e); - } + public function dispatch(event:T) { + if (root == null) { + throw "no root component"; } - } - private function linkViaRTTI(force:Bool = false) { + root.dispatch(event); + } + + public function registerEvent(type:EventType, listener:T->Void, priority:Int = 0) { if (root == null) { - return; - } - var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this)); - for (f in rtti.fields) { - switch (f.type) { - case CClass(name, params): - if (ComponentClassMap.instance.hasClassName(name)) { - var candidate = root.findComponent(f.name); - if (force) { - Reflect.setField(this, f.name, null); - } - if (candidate != null && Reflect.field(this, f.name) == null) { - Reflect.setField(this, f.name, candidate); - } - } - case CFunction(args, ret): - var m = getMetaRTTI(f.meta, "bind"); - if (m != null) { - var candidate:Component = root.findComponent(m.params[0]); - if (candidate != null) { - var parts = m.params[1].split("."); - var candidateEvent = "haxe.ui.events." + parts[0]; - var c = Type.resolveClass(candidateEvent); - if (c != null) { - var eventString = Reflect.field(c, parts[1]); - var fn = Reflect.field(this, f.name); - candidate.registerEvent(eventString, fn); - } - - } - } - case _: - } + throw "no root component"; } - } - private function getMetaRTTI(metadata:MetaData, name:String):{name:String, params:Array} { - for (m in metadata) { - if (m.name == name || m.name == ":" + name) { - return m; - } - } - return null; - } + root.registerEvent(type, listener, priority); + } - private function getMetasRTTI(metadata:MetaData, name:String):Array<{name:String, params:Array}> { - var metas = []; - for (m in metadata) { - if (m.name == name || m.name == ":" + name) { - metas.push(m); - } + public function show() { + if (root == null) { + throw "no root component"; } - return metas; + root.show(); } - private function getMetaWithValueRTTI(metadata:MetaData, name:String, value:String, paramIndex:Int = 0):{name:String, params:Array} { - for (m in metadata) { - if (m.name == name || m.name == ":" + name) { - if (m.params[paramIndex].startsWith(value)) { - return m; - } - } + public function hide() { + if (root == null) { + throw "no root component"; } - return null; + root.hide(); } public override function destroy() { if (root != null) { - remove(root); + Screen.instance.removeComponent(root); } + super.destroy(); root = null; } } \ No newline at end of file diff --git a/haxe/ui/backend/flixel/UIRuntimeSubState.hx b/haxe/ui/backend/flixel/UIRuntimeSubState.hx index 762e626..d9346c0 100644 --- a/haxe/ui/backend/flixel/UIRuntimeSubState.hx +++ b/haxe/ui/backend/flixel/UIRuntimeSubState.hx @@ -1,10 +1,10 @@ package haxe.ui.backend.flixel; -import haxe.rtti.CType; -import haxe.ui.RuntimeComponentBuilder; import haxe.ui.core.Component; -import haxe.ui.core.ComponentClassMap; import haxe.ui.core.Screen; +import haxe.ui.events.UIEvent; +import haxe.ui.events.EventType; +import haxe.ui.backend.flixel.UIRTTITools.*; using StringTools; @@ -12,22 +12,26 @@ using StringTools; class UIRuntimeSubState extends UISubStateBase { // uses rtti to "build" a class with a similar experience to using macros public var root:Component; - public var assetId:String; - - public function new(assetId:String = null) { + public function new() { super(); - this.assetId = assetId; } public override function create() { - buildViaRTTI(); - linkViaRTTI(); + var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this)); + root = buildViaRTTI(rtti); + linkViaRTTI(rtti, this, root); if (root != null) { + root.registerEvent(UIEvent.READY, (_) -> { + onReady(); + }); Screen.instance.addComponent(root); } super.create(); } + private function onReady() { + } + ///////////////////////////////////////////////////////////////////////////////////////////////// // util functions ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -79,117 +83,41 @@ class UIRuntimeSubState extends UISubStateBase { // uses rtti to "build" a class return root.findComponentsUnderPoint(screenX, screenY, type); } - ///////////////////////////////////////////////////////////////////////////////////////////////// - // rtti functions - ///////////////////////////////////////////////////////////////////////////////////////////////// - private function buildViaRTTI() { - var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this)); - var m = getMetaWithValueRTTI(rtti.meta, "build", "haxe.ui.RuntimeComponentBuilder.build"); - if (m != null) { - assetId = m.params[0].replace("haxe.ui.RuntimeComponentBuilder.build(", "").replace(")", ""); - assetId = assetId.replace("\"", ""); - assetId = assetId.replace("'", ""); - this.root = RuntimeComponentBuilder.fromAsset(assetId); - } - m = getMetaRTTI(rtti.meta, "xml"); - if (m != null) { // comes back as an escaped CDATA section - var xmlString = m.params[0].trim(); - if (xmlString.startsWith("")) { - xmlString = xmlString.substring(0, xmlString.length - "]]>".length); - } - if (xmlString.startsWith("\"")) { - xmlString = xmlString.substring(1); - } - if (xmlString.endsWith("\"")) { - xmlString = xmlString.substring(0, xmlString.length - 1); - } - xmlString = xmlString.replace("\\r", "\r"); - xmlString = xmlString.replace("\\n", "\n"); - xmlString = xmlString.replace("\\\"", "\""); - xmlString = xmlString.trim(); - try { - this.root = RuntimeComponentBuilder.fromString(xmlString); - } catch (e:Dynamic) { - trace("ERROR", e); - } + public function dispatch(event:T) { + if (root == null) { + throw "no root component"; } - } - private function linkViaRTTI(force:Bool = false) { + root.dispatch(event); + } + + public function registerEvent(type:EventType, listener:T->Void, priority:Int = 0) { if (root == null) { - return; - } - var rtti = haxe.rtti.Rtti.getRtti(Type.getClass(this)); - for (f in rtti.fields) { - switch (f.type) { - case CClass(name, params): - if (ComponentClassMap.instance.hasClassName(name)) { - var candidate = root.findComponent(f.name); - if (force) { - Reflect.setField(this, f.name, null); - } - if (candidate != null && Reflect.field(this, f.name) == null) { - Reflect.setField(this, f.name, candidate); - } - } - case CFunction(args, ret): - var m = getMetaRTTI(f.meta, "bind"); - if (m != null) { - var candidate:Component = root.findComponent(m.params[0]); - if (candidate != null) { - var parts = m.params[1].split("."); - var candidateEvent = "haxe.ui.events." + parts[0]; - var c = Type.resolveClass(candidateEvent); - if (c != null) { - var eventString = Reflect.field(c, parts[1]); - var fn = Reflect.field(this, f.name); - candidate.registerEvent(eventString, fn); - } - - } - } - case _: - } + throw "no root component"; } - } - private function getMetaRTTI(metadata:MetaData, name:String):{name:String, params:Array} { - for (m in metadata) { - if (m.name == name || m.name == ":" + name) { - return m; - } - } - return null; - } + root.registerEvent(type, listener, priority); + } - private function getMetasRTTI(metadata:MetaData, name:String):Array<{name:String, params:Array}> { - var metas = []; - for (m in metadata) { - if (m.name == name || m.name == ":" + name) { - metas.push(m); - } + public function show() { + if (root == null) { + throw "no root component"; } - return metas; + root.show(); } - private function getMetaWithValueRTTI(metadata:MetaData, name:String, value:String, paramIndex:Int = 0):{name:String, params:Array} { - for (m in metadata) { - if (m.name == name || m.name == ":" + name) { - if (m.params[paramIndex].startsWith(value)) { - return m; - } - } + public function hide() { + if (root == null) { + throw "no root component"; } - return null; + root.hide(); } public override function destroy() { if (root != null) { - remove(root); + Screen.instance.removeComponent(root); } + super.destroy(); root = null; } } \ No newline at end of file diff --git a/haxe/ui/backend/flixel/UIState.hx b/haxe/ui/backend/flixel/UIState.hx index 04034a1..20ca40a 100644 --- a/haxe/ui/backend/flixel/UIState.hx +++ b/haxe/ui/backend/flixel/UIState.hx @@ -1,11 +1,13 @@ package haxe.ui.backend.flixel; -import haxe.ui.layouts.LayoutFactory; import haxe.ui.containers.Box; import haxe.ui.core.Component; +import haxe.ui.events.UIEvent; +import haxe.ui.layouts.LayoutFactory; @:autoBuild(haxe.ui.macros.Macros.buildBehaviours()) @:autoBuild(haxe.ui.macros.Macros.build()) +@:autoBuild(haxe.ui.backend.flixel.macros.UIStateMacro.checkDefine()) class UIState extends UIStateBase { // must use -D haxeui_dont_impose_base_class public var bindingRoot:Bool = false; @@ -25,7 +27,9 @@ class UIState extends UIStateBase { // must use -D haxeui_dont_impose_base_class public override function create() { super.create(); - + root.registerEvent(UIEvent.READY, (_) -> { + onReady(); + }); add(root); } @@ -87,4 +91,12 @@ class UIState extends UIStateBase { // must use -D haxeui_dont_impose_base_class root.styleString = value; return value; } -} \ No newline at end of file + + public function show() { + root.show(); + } + + public function hide() { + root.hide(); + } +} diff --git a/haxe/ui/backend/flixel/UISubState.hx b/haxe/ui/backend/flixel/UISubState.hx index 7c304a0..0bf236c 100644 --- a/haxe/ui/backend/flixel/UISubState.hx +++ b/haxe/ui/backend/flixel/UISubState.hx @@ -3,9 +3,11 @@ package haxe.ui.backend.flixel; import haxe.ui.layouts.LayoutFactory; import haxe.ui.containers.Box; import haxe.ui.core.Component; +import haxe.ui.events.UIEvent; @:autoBuild(haxe.ui.macros.Macros.buildBehaviours()) @:autoBuild(haxe.ui.macros.Macros.build()) +@:autoBuild(haxe.ui.backend.flixel.macros.UIStateMacro.checkDefine()) class UISubState extends UISubStateBase { // must use -D haxeui_dont_impose_base_class public var bindingRoot:Bool = false; @@ -25,7 +27,9 @@ class UISubState extends UISubStateBase { // must use -D haxeui_dont_impose_base public override function create() { super.create(); - + root.registerEvent(UIEvent.READY, (_) -> { + onReady(); + }); add(root); } @@ -87,4 +91,13 @@ class UISubState extends UISubStateBase { // must use -D haxeui_dont_impose_base root.styleString = value; return value; } -} \ No newline at end of file + + public function show() { + root.show(); + } + + public function hide() { + root.hide(); + } + +} diff --git a/haxe/ui/backend/flixel/components/SparrowPlayer.hx b/haxe/ui/backend/flixel/components/SparrowPlayer.hx index 00c374d..c2c0176 100644 --- a/haxe/ui/backend/flixel/components/SparrowPlayer.hx +++ b/haxe/ui/backend/flixel/components/SparrowPlayer.hx @@ -1,15 +1,15 @@ package haxe.ui.backend.flixel.components; -import haxe.ui.data.DataSource; -import haxe.ui.core.IDataComponent; -import haxe.ui.events.AnimationEvent; -import haxe.ui.core.Component; -import haxe.ui.geom.Size; -import haxe.ui.layouts.DefaultLayout; import flixel.FlxSprite; import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxFramesCollection; import haxe.ui.containers.Box; +import haxe.ui.core.Component; +import haxe.ui.core.IDataComponent; +import haxe.ui.data.DataSource; +import haxe.ui.events.AnimationEvent; +import haxe.ui.geom.Size; +import haxe.ui.layouts.DefaultLayout; import openfl.Assets; private typedef AnimationInfo = { @@ -38,7 +38,6 @@ class SparrowPlayer extends Box implements IDataComponent { public function new() { super(); - _overrideSkipTransformChildren = false; sprite = new FlxSprite(1, 1); add(sprite); } @@ -66,11 +65,10 @@ class SparrowPlayer extends Box implements IDataComponent { } private var _dataSource:DataSource = null; - public var dataSource(get, set):DataSource; - private function get_dataSource():DataSource { + private override function get_dataSource():DataSource { return _dataSource; } - private function set_dataSource(value:DataSource):DataSource { + private override function set_dataSource(value:DataSource):DataSource { _dataSource = value; for (i in 0..._dataSource.size) { var item:Dynamic = _dataSource.get(i); @@ -138,8 +136,6 @@ class SparrowPlayer extends Box implements IDataComponent { _redispatchStart = false; dispatch(new AnimationEvent(AnimationEvent.START)); } - - parentComponent._overrideSkipTransformChildren = false; } private var _cachedAnimationPrefixes:Array = []; // component might not have an animation yet, so we'll cache it if thats the case @@ -173,12 +169,21 @@ class SparrowPlayer extends Box implements IDataComponent { var frames:FlxFramesCollection = FlxAtlasFrames.fromSparrow(png, Assets.getText(xml)); sprite.frames = frames; + #if (flixel >= "5.9.0") + if (!sprite.animation.onFrameChange.has(onFrame)) { + sprite.animation.onFrameChange.add(onFrame); + } + if (!sprite.animation.onFinish.has(onFinish)) { + sprite.animation.onFinish.add(onFinish); + } + #else if (sprite.animation.callback == null) { sprite.animation.callback = onFrame; } if (sprite.animation.finishCallback == null) { sprite.animation.finishCallback = onFinish; } + #end invalidateComponentLayout(); _animationLoaded = true; @@ -253,8 +258,8 @@ class SparrowPlayer extends Box implements IDataComponent { private override function repositionChildren() { super.repositionChildren(); - sprite.x = this.screenX; - sprite.y = this.screenY; + sprite.x = this.cachedScreenX; + sprite.y = this.cachedScreenY; } } diff --git a/haxe/ui/backend/flixel/components/SpriteWrapper.hx b/haxe/ui/backend/flixel/components/SpriteWrapper.hx new file mode 100644 index 0000000..cd1e843 --- /dev/null +++ b/haxe/ui/backend/flixel/components/SpriteWrapper.hx @@ -0,0 +1,65 @@ +package haxe.ui.backend.flixel.components; + +import flixel.FlxSprite; +import flixel.math.FlxRect; +import haxe.ui.containers.Box; +import haxe.ui.core.Component; +import haxe.ui.geom.Size; +import haxe.ui.layouts.DefaultLayout; + +@:composite(Layout) +class SpriteWrapper extends Box { + public var spriteOffsetX:Float = 0; + public var spriteOffsetY:Float = 0; + + private var _sprite:FlxSprite = null; + public var sprite(get, set):FlxSprite; + private function get_sprite():FlxSprite { + return _sprite; + } + private function set_sprite(value:FlxSprite):FlxSprite { + if (_sprite != null) { + remove(_sprite); + } + _sprite = value; + add(_sprite); + invalidateComponentLayout(); + return value; + } + + private override function repositionChildren() { + super.repositionChildren(); + if (sprite != null) { + sprite.x = spriteOffsetX + this.cachedScreenX; + sprite.y = spriteOffsetY + this.cachedScreenY; + } + } +} + +@:access(haxe.ui.backend.flixel.components.SpriteWrapper) +private class Layout extends DefaultLayout { + public override function resizeChildren() { + super.resizeChildren(); + + var wrapper = cast(_component, SpriteWrapper); + var sprite = wrapper.sprite; + if (sprite == null) { + return super.resizeChildren(); + } + + sprite.origin.set(0, 0); + sprite.setGraphicSize(Std.int(innerWidth), Std.int(innerHeight)); + } + + public override function calcAutoSize(exclusions:Array = null):Size { + var wrapper = cast(_component, SpriteWrapper); + var sprite = wrapper.sprite; + if (sprite == null) { + return super.calcAutoSize(exclusions); + } + var size = new Size(); + size.width = sprite.width + paddingLeft + paddingRight; + size.height = sprite.height + paddingTop + paddingBottom; + return size; + } +} \ No newline at end of file diff --git a/haxe/ui/backend/flixel/macros/UIStateMacro.hx b/haxe/ui/backend/flixel/macros/UIStateMacro.hx new file mode 100644 index 0000000..cf20fc4 --- /dev/null +++ b/haxe/ui/backend/flixel/macros/UIStateMacro.hx @@ -0,0 +1,42 @@ +package haxe.ui.backend.flixel.macros; + +#if macro +import haxe.macro.Expr.Field; +import haxe.macro.Context; + +/** + * Macro which makes an error at compile time if the user attemps to use `UIState` or `UISubState` without having `haxeui_dont_impose_base_class` defined. + */ +class UIStateMacro { + public static function checkDefine():Array { + var localClass:String = Context.getLocalClass().get().name; + + if (!Context.defined("haxeui_dont_impose_base_class")) + Context.error("You must define haxeui_dont_impose_base_class in order to use " + findUIClass() + " (for class " + localClass + ")", Context.currentPos()); + + return Context.getBuildFields(); + } + + static function findUIClass():String { + var cls = Context.getLocalClass().get(); + + while (!isUIState(cls.name)) { + if (cls.superClass == null) + break; + + cls = cls.superClass.t.get(); + } + + if (!isUIState(cls.name)) { + // shouldn't happen, but just in case + return "UI states"; + } + + return cls.name; + } + + static function isUIState(name:String):Bool { + return name == "UIState" || name == "UISubState"; + } +} +#end diff --git a/haxe/ui/backend/flixel/textinputs/FlxTextInput.hx b/haxe/ui/backend/flixel/textinputs/FlxTextInput.hx new file mode 100644 index 0000000..e3bda74 --- /dev/null +++ b/haxe/ui/backend/flixel/textinputs/FlxTextInput.hx @@ -0,0 +1,398 @@ +package haxe.ui.backend.flixel.textinputs; + +import flixel.FlxSprite; +import flixel.text.FlxInputText; +import flixel.util.FlxColor; +import haxe.ui.backend.TextInputImpl.TextInputEvent; +import haxe.ui.core.Component; +import openfl.events.Event; +import openfl.events.KeyboardEvent; + +#if (flixel >= "5.9.0") +class FlxTextInput extends TextBase { + public static var USE_ON_ADDED:Bool = false; + public static var USE_ON_REMOVED:Bool = false; + + private static inline var PADDING_X:Int = 4; + private static inline var PADDING_Y:Int = 2; + + private var tf:FlxInputText; + + public function new() { + super(); + tf = new FlxInputText(0, 0, 0, null, 8, FlxColor.BLACK, FlxColor.TRANSPARENT); + tf.onTextChange.add(onInternalChange); + tf.onScrollChange.add(onScroll); + tf.pixelPerfectRender = true; + _inputData.vscrollPageStep = 1; + _inputData.vscrollNativeWheel = true; + } + + public override function focus() { + tf.startFocus(); + } + + public override function blur() { + tf.endFocus(); + } + + public function attach() { + } + + public var visible(get, set):Bool; + private function get_visible():Bool { + return tf.visible; + } + private function set_visible(value:Bool):Bool { + tf.active = tf.visible = value; // text input shouldn't be active if it's hidden + return value; + } + + public var x(get, set):Float; + private function get_x():Float { + return tf.x; + } + private function set_x(value:Float):Float { + tf.x = value; + return value; + } + + public var y(get, set):Float; + private function get_y():Float { + return tf.y; + } + private function set_y(value:Float):Float { + tf.y = value; + return value; + } + + public var scaleX(get, set):Float; + private function get_scaleX():Float { + return tf.scale.x; + } + private function set_scaleX(value:Float):Float { + // do nothing + return value; + } + + public var scaleY(get, set):Float; + private function get_scaleY():Float { + return tf.scale.y; + } + private function set_scaleY(value:Float):Float { + // do nothing + return value; + } + + public var alpha(get, set):Float; + private function get_alpha():Float { + return tf.alpha; + } + private function set_alpha(value:Float):Float { + tf.alpha = value; + return value; + } + + private override function validateData() { + if (_text != null) { + if (_dataSource == null) { + tf.text = normalizeText(_text); + } + } + + var hscrollValue = Std.int(_inputData.hscrollPos); + if (tf.scrollH != hscrollValue) { + tf.scrollH = hscrollValue; + } + + var vscrollValue = Std.int(_inputData.vscrollPos) + 1; + if (tf.scrollV != vscrollValue) { + tf.scrollV = vscrollValue; + } + } + + private function normalizeText(text:String):String { + text = StringTools.replace(text, "\\n", "\n"); + return text; + } + + private override function measureText() { + //tf.width = _width * Toolkit.scaleX; + _textHeight = tf.textField.textHeight; + if (_textHeight == 0) { + var tmpText:String = tf.text; + tf.text = "|"; + _textHeight = tf.textField.textHeight; + tf.text = tmpText; + } + + _textWidth = Math.round(_textWidth) / Toolkit.scaleX; + _textHeight = Math.round(_textHeight) / Toolkit.scaleY; + + ////////////////////////////////////////////////////////////////////////////// + + _inputData.hscrollMax = tf.maxScrollH; + // see below + _inputData.hscrollPageSize = (_width * _inputData.hscrollMax) / _textWidth; + + _inputData.vscrollMax = tf.maxScrollV - 1; + _inputData.vscrollPageSize = (_height * _inputData.vscrollMax) / _textHeight; + } + + private override function validateStyle():Bool { + var measureTextRequired:Bool = false; + + if (_textStyle != null) { + var textAlign = (_textStyle.textAlign != null ? _textStyle.textAlign : "left"); + if (tf.alignment != textAlign) { + tf.alignment = textAlign; + } + + var fontSizeValue = Std.int(_textStyle.fontSize); + if (tf.size != fontSizeValue) { + tf.size = Std.int(fontSizeValue * Toolkit.scale); + + measureTextRequired = true; + } + + if (_fontInfo != null && tf.font != _fontInfo.data) { + tf.font = _fontInfo.data; + measureTextRequired = true; + } + + if (tf.color != _textStyle.color) { + tf.color = _textStyle.color; + } + + var fontBold = (_textStyle.fontBold != null ? _textStyle.fontBold : false); + if (tf.bold != fontBold) { + tf.bold = fontBold; + measureTextRequired = true; + } + + var fontItalic = (_textStyle.fontItalic != null ? _textStyle.fontItalic : false); + if (tf.italic != fontItalic) { + tf.italic = fontItalic; + measureTextRequired = true; + } + } + + if (tf.wordWrap != _displayData.wordWrap) { + tf.wordWrap = _displayData.wordWrap; + measureTextRequired = true; + } + + if (tf.multiline != _displayData.multiline) { + tf.multiline = _displayData.multiline; + // `multiline` only decides whether the user can add new lines, + // so measuring the text is not required. + } + + if (tf.passwordMode != _inputData.password) { + tf.passwordMode = _inputData.password; + measureTextRequired = true; + } + + tf.editable = !parentComponent.disabled; + + return measureTextRequired; + } + + private override function validatePosition() { + _left = Math.round(_left * Toolkit.scaleX); + _top = Math.round(_top * Toolkit.scaleY); + } + + private override function validateDisplay() { + if (_width <= 0 || _height <= 0) { + return; + } + + if (tf.width != _width * Toolkit.scaleX) { + tf.width = _width * Toolkit.scaleX; + tf.fieldWidth = tf.width; + } + + if (tf.height != (_height + PADDING_Y) * Toolkit.scaleY) { + tf.height = (_height + PADDING_Y) * Toolkit.scaleY; + tf.fieldHeight = tf.height; + } + } + + private var _onMouseDown:TextInputEvent->Void = null; + public var onMouseDown(null, set):TextInputEvent->Void; + private function set_onMouseDown(value:TextInputEvent->Void):TextInputEvent->Void { + if (_onMouseDown != null) { + //tf.removeEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseDownEvent); + } + _onMouseDown = value; + if (_onMouseDown != null) { + //tf.addEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseDownEvent); + } + return value; + } + + /* + private function __onTextInputMouseDownEvent(event:openfl.events.MouseEvent) { + if (_onMouseDown != null) { + _onMouseDown({ + type: event.type, + stageX: event.stageX, + stageY: event.stageY + }); + } + } + */ + + private var _onMouseUp:TextInputEvent->Void = null; + public var onMouseUp(null, set):TextInputEvent->Void; + private function set_onMouseUp(value:TextInputEvent->Void):TextInputEvent->Void { + if (_onMouseUp != null) { + //tf.removeEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseUpEvent); + } + _onMouseUp = value; + if (_onMouseUp != null) { + //tf.addEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseUpEvent); + } + return value; + } + + /* + private function __onTextInputMouseUpEvent(event:openfl.events.MouseEvent) { + if (_onMouseUp != null) { + _onMouseUp({ + type: event.type, + stageX: event.stageX, + stageY: event.stageY + }); + } + } + */ + + public function equals(sprite:FlxSprite):Bool { + return sprite == tf; + } + + private var _onClick:TextInputEvent->Void = null; + public var onClick(null, set):TextInputEvent->Void; + private function set_onClick(value:TextInputEvent->Void):TextInputEvent->Void { + if (_onClick != null) { + //tf.removeEventListener(openfl.events.MouseEvent.CLICK, __onTextInputClickEvent); + } + _onClick = value; + if (_onClick != null) { + //tf.addEventListener(openfl.events.MouseEvent.CLICK, __onTextInputClickEvent); + } + return value; + } + + /* + private function __onTextInputClickEvent(event:openfl.events.MouseEvent) { + if (_onClick != null) { + _onClick({ + type: event.type, + stageX: event.stageX, + stageY: event.stageY + }); + } + } + */ + + private var _onChange:TextInputEvent->Void = null; + public var onChange(null, set):TextInputEvent->Void; + private function set_onChange(value:TextInputEvent->Void):TextInputEvent->Void { + if (_onChange != null) { + tf.onTextChange.remove(__onTextInputChangeEvent); + } + _onChange = value; + if (_onChange != null) { + tf.onTextChange.add(__onTextInputChangeEvent); + } + return value; + } + + private function __onTextInputChangeEvent(text:String, action:FlxInputTextChange) { + if (_onChange != null) { + _onChange({ + type: "change", + stageX: 0, + stageY: 0 + }); + } + } + + private var _onKeyDown:KeyboardEvent->Void = null; + public var onKeyDown(null, set):KeyboardEvent->Void; + private function set_onKeyDown(value:KeyboardEvent->Void):KeyboardEvent->Void { + if (_onKeyDown != null) { + //tf.textField.removeEventListener(KeyboardEvent.KEY_DOWN, __onTextInputKeyDown); + } + _onKeyDown = value; + if (_onKeyDown != null) { + //tf.textField.addEventListener(KeyboardEvent.KEY_DOWN, __onTextInputKeyDown); + } + return value; + } + + /* + private function __onTextInputKeyDown(e:KeyboardEvent) { + if (_onKeyDown != null) + _onKeyDown(e); + } + */ + + private var _onKeyUp:KeyboardEvent->Void = null; + public var onKeyUp(null, set):KeyboardEvent->Void; + private function set_onKeyUp(value:KeyboardEvent->Void):KeyboardEvent->Void { + if (_onKeyUp != null) { + //tf.textField.removeEventListener(KeyboardEvent.KEY_UP, __onTextInputKeyUp); + } + _onKeyUp = value; + if (_onKeyUp != null) { + //tf.textField.addEventListener(KeyboardEvent.KEY_UP, __onTextInputKeyUp); + } + return value; + } + + /* + private function __onTextInputKeyUp(e:KeyboardEvent) { + if (_onKeyUp != null) + _onKeyUp(e); + } + */ + + private function onInternalChange(text:String, action:FlxInputTextChange) { + _text = tf.text; + + measureText(); + + if (_inputData.onChangedCallback != null) { + _inputData.onChangedCallback(); + } + } + + private function onScroll(scrollH:Int, scrollV:Int) { + _inputData.hscrollPos = tf.scrollH; + _inputData.vscrollPos = tf.scrollV - 1; + + if (_inputData.onScrollCallback != null) { + _inputData.onScrollCallback(); + } + } + + public function update() { + } + + public function addToComponent(component:Component) { + //StateHelper.currentState.add(tf); + component.add(tf); + } + + public function destroy(component:Component) { + tf.visible = false; + component.remove(tf, true); + tf.destroy(); + tf = null; + } +} +#end \ No newline at end of file diff --git a/haxe/ui/backend/flixel/textinputs/OpenFLTextInput.hx b/haxe/ui/backend/flixel/textinputs/OpenFLTextInput.hx index 8c894ec..086876b 100644 --- a/haxe/ui/backend/flixel/textinputs/OpenFLTextInput.hx +++ b/haxe/ui/backend/flixel/textinputs/OpenFLTextInput.hx @@ -1,18 +1,24 @@ package haxe.ui.backend.flixel.textinputs; import flixel.FlxG; +import flixel.FlxSprite; import haxe.ui.Toolkit; +import haxe.ui.backend.TextInputImpl.TextInputEvent; import haxe.ui.core.Component; import haxe.ui.core.Screen; import haxe.ui.events.UIEvent; import haxe.ui.geom.Rectangle; import openfl.events.Event; +import openfl.events.KeyboardEvent; import openfl.text.TextField; import openfl.text.TextFieldAutoSize; import openfl.text.TextFieldType; import openfl.text.TextFormat; class OpenFLTextInput extends TextBase { + public static var USE_ON_ADDED:Bool = true; + public static var USE_ON_REMOVED:Bool = true; + private var PADDING_X:Int = 4; private var PADDING_Y:Int = 0; @@ -29,24 +35,217 @@ class OpenFLTextInput extends TextBase { tf.wordWrap = true; tf.tabEnabled = false; //tf.stage.focus = null; - tf.addEventListener(Event.CHANGE, onChange); + tf.addEventListener(Event.CHANGE, onInternalChange); + _inputData.vscrollPageStep = 1; + _inputData.vscrollNativeWheel = true; } public override function focus() { if (tf.stage != null) { - tf.stage.focus = tf; + //tf.stage.focus = tf; } } public override function blur() { if (tf.stage != null) { - tf.stage.focus = null; + //tf.stage.focus = null; } } public function attach() { } + public var visible(get, set):Bool; + private function get_visible():Bool { + return tf.visible; + } + private function set_visible(value:Bool):Bool { + tf.visible = value; + return value; + } + + public var x(get, set):Float; + private function get_x():Float { + return tf.x; + } + private function set_x(value:Float):Float { + tf.x = value * FlxG.scaleMode.scale.x; + return value; + } + + public var y(get, set):Float; + private function get_y():Float { + return tf.y; + } + private function set_y(value:Float):Float { + tf.y = value * FlxG.scaleMode.scale.y; + return value; + } + + public var scaleX(get, set):Float; + private function get_scaleX():Float { + return tf.scaleX; + } + private function set_scaleX(value:Float):Float { + tf.scaleX = value; + return value; + } + + public var scaleY(get, set):Float; + private function get_scaleY():Float { + return tf.scaleY; + } + private function set_scaleY(value:Float):Float { + tf.scaleY = value; + return value; + } + + public var alpha(get, set):Float; + private function get_alpha():Float { + return tf.alpha; + } + private function set_alpha(value:Float):Float { + tf.alpha = value; + return value; + } + + + private var _onMouseDown:TextInputEvent->Void = null; + public var onMouseDown(null, set):TextInputEvent->Void; + private function set_onMouseDown(value:TextInputEvent->Void):TextInputEvent->Void { + if (_onMouseDown != null) { + tf.removeEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseDownEvent); + } + _onMouseDown = value; + if (_onMouseDown != null) { + tf.addEventListener(openfl.events.MouseEvent.MOUSE_DOWN, __onTextInputMouseDownEvent); + } + return value; + } + + private function __onTextInputMouseDownEvent(event:openfl.events.MouseEvent) { + if (_onMouseDown != null) { + _onMouseDown({ + type: event.type, + stageX: event.stageX, + stageY: event.stageY + }); + } + } + + private var _onMouseUp:TextInputEvent->Void = null; + public var onMouseUp(null, set):TextInputEvent->Void; + private function set_onMouseUp(value:TextInputEvent->Void):TextInputEvent->Void { + if (_onMouseUp != null) { + tf.removeEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseUpEvent); + } + _onMouseUp = value; + if (_onMouseUp != null) { + tf.addEventListener(openfl.events.MouseEvent.MOUSE_UP, __onTextInputMouseUpEvent); + } + return value; + } + + private function __onTextInputMouseUpEvent(event:openfl.events.MouseEvent) { + if (_onMouseUp != null) { + _onMouseUp({ + type: event.type, + stageX: event.stageX, + stageY: event.stageY + }); + } + } + + private var _onClick:TextInputEvent->Void = null; + public var onClick(null, set):TextInputEvent->Void; + private function set_onClick(value:TextInputEvent->Void):TextInputEvent->Void { + if (_onClick != null) { + tf.removeEventListener(openfl.events.MouseEvent.CLICK, __onTextInputClickEvent); + } + _onClick = value; + if (_onClick != null) { + tf.addEventListener(openfl.events.MouseEvent.CLICK, __onTextInputClickEvent); + } + return value; + } + + private function __onTextInputClickEvent(event:openfl.events.MouseEvent) { + if (_onClick != null) { + _onClick({ + type: event.type, + stageX: event.stageX, + stageY: event.stageY + }); + } + } + + private var _onChange:TextInputEvent->Void = null; + public var onChange(null, set):TextInputEvent->Void; + private function set_onChange(value:TextInputEvent->Void):TextInputEvent->Void { + if (_onChange != null) { + tf.removeEventListener(Event.CHANGE, __onTextInputChangeEvent); + } + _onChange = value; + if (_onChange != null) { + tf.addEventListener(Event.CHANGE, __onTextInputChangeEvent); + } + return value; + } + + private function __onTextInputChangeEvent(event:Event) { + if (_onChange != null) { + _onChange({ + type: event.type, + stageX: 0, + stageY: 0 + }); + } + } + + private var _onKeyDown:KeyboardEvent->Void = null; + public var onKeyDown(null, set):KeyboardEvent->Void; + private function set_onKeyDown(value:KeyboardEvent->Void):KeyboardEvent->Void { + if (_onKeyDown != null) { + tf.removeEventListener(KeyboardEvent.KEY_DOWN, __onTextInputKeyDown); + } + _onKeyDown = value; + if (_onKeyDown != null) { + tf.addEventListener(KeyboardEvent.KEY_DOWN, __onTextInputKeyDown); + } + return value; + } + + private function __onTextInputKeyDown(e:KeyboardEvent) { + if (_onKeyDown != null) + _onKeyDown(e); + } + + private var _onKeyUp:KeyboardEvent->Void = null; + public var onKeyUp(null, set):KeyboardEvent->Void; + private function set_onKeyUp(value:KeyboardEvent->Void):KeyboardEvent->Void { + if (_onKeyUp != null) { + tf.removeEventListener(KeyboardEvent.KEY_UP, __onTextInputKeyUp); + } + _onKeyUp = value; + if (_onKeyUp != null) { + tf.addEventListener(KeyboardEvent.KEY_UP, __onTextInputKeyUp); + } + return value; + } + + private function __onTextInputKeyUp(e:KeyboardEvent) { + if (_onKeyUp != null) + _onKeyUp(e); + } + + public function addToComponent(component:Component) { + FlxG.addChildBelowMouse(tf, 0xffffff); + } + + public function equals(sprite:FlxSprite):Bool { + return false; + } + private var _parentHidden:Bool = false; public function update() { var ref = parentComponent; @@ -89,14 +288,16 @@ class OpenFLTextInput extends TextBase { } } + /* if (overlaps == true && tf.visible == true) { tf.visible = false; } else if (overlaps == false && tf.visible == false) { tf.visible = true; } + */ } - public function destroy() { + public function destroy(component:Component) { _parentHidden = true; tf.visible = false; FlxG.removeChild(tf); @@ -147,7 +348,7 @@ class OpenFLTextInput extends TextBase { // see below _inputData.hscrollPageSize = (_width * _inputData.hscrollMax) / _textWidth; - _inputData.vscrollMax = tf.maxScrollV; + _inputData.vscrollMax = tf.maxScrollV - 1; // cant have page size yet as there seems to be an openfl issue with bottomScrollV // https://github.com/openfl/openfl/issues/2220 _inputData.vscrollPageSize = (_height * _inputData.vscrollMax) / _textHeight; @@ -213,11 +414,13 @@ class OpenFLTextInput extends TextBase { if (tf.displayAsPassword != _inputData.password) { tf.displayAsPassword = _inputData.password; } + + tf.type = (parentComponent.disabled ? DYNAMIC : INPUT); return measureTextRequired; } - private function onChange(e) { + private function onInternalChange(e:Event) { _text = tf.text; measureText(); diff --git a/haxelib.json b/haxelib.json index d3d1f90..b7fc9d9 100644 --- a/haxelib.json +++ b/haxelib.json @@ -1,21 +1,21 @@ { + "version": "1.7.0", "contributors": [ "haxeui", "MSGhero", "ianharrigan" ], - "version": "1.6.0", - "releasenote": "1.6.0 release", + "dependencies": { + "haxeui-core": "", + "flixel": "" + }, + "license": "MIT", "tags": [ "ui", "gui", "flixel" ], - "license": "MIT", - "dependencies": { - "flixel": "", - "haxeui-core": "" - }, + "releasenote": "1.7.0 release", "name": "haxeui-flixel", "description": "The Flixel backend of the HaxeUI framework", "url": "https://github.com/haxeui/haxeui-flixel"