haxeui-core icon indicating copy to clipboard operation
haxeui-core copied to clipboard

More elegant way to override events?

Open ianharrigan opened this issue 3 years ago • 0 comments

Consider the following test app:

<grid>
    <label text="Broken:" verticalAlign="center" />
    <custom-component-broken id="broken" theText="Bob" />
    
    <label text="Fix #1:" verticalAlign="center" />
    <custom-component-fix1 id="fix1" theText="Tim" />
</grid>    

which is used using:

@:bind(broken, UIEvent.CHANGE)
function onBrokenChange(e) {
    trace("broken text changed");
}

@:bind(broken, MouseEvent.CLICK) 
private function onBrokenClick(e) {
    broken.theText = "clicked!";
}

@:bind(fix1, UIEvent.CHANGE)
function onFix1Change(e) {
    trace("fix1 text changed");
}

@:bind(fix1, MouseEvent.CLICK) 
private function onFix1Click(e) {
    fix1.theText = "clicked!";
}

where:

custom-component.xml

<hbox>
    <textfield id="theTextField" />
    <button id="clearButton" text="Clear" />
    <button id="actionButton" text="Action" />
</hbox>

CustomComponentBroken.hx

@:build(haxe.ui.macros.ComponentMacros.build("assets/custom-component.xml"))
class CustomComponentBroken extends HBox {
    @:bind(theTextField.text) var theText:String;
    
    public function new() {
        super();
    }

    @:bind(theTextField, UIEvent.CHANGE)
    function onTextChange(e:UIEvent) {
        dispatch(e);
    }
    
    @:bind(clearButton, MouseEvent.CLICK)
    function onClear(e) {
        theText = "";
    }
}

CustomComponentFix1.hx

@:build(haxe.ui.macros.ComponentMacros.build("assets/custom-component.xml"))
class CustomComponentFix1 extends HBox {
    @:bind(theTextField.text) var theText:String;
    
    public function new() {
        super();
    }

    @:bind(theTextField, UIEvent.CHANGE)
    function onTextChange(e:UIEvent) {
        dispatch(e);
    }
    
    @:bind(clearButton, MouseEvent.CLICK)
    function onClear(e) {
        theTextField.text = "";
    }
    
    public override function registerEvent(type, listener, priority = 0) {
        if (type == MouseEvent.CLICK) {
            actionButton.registerEvent(type, listener, priority);
        } else {
            super.registerEvent(type, listener, priority);
        }
    }
}

The result of "broken" is:

issue-1

This is because the click event is fired off for the whole component, which sets the text of the text area... so even if you click the clear button, it clears the text, then instantly sets it back to "clicked!". One way round this is shown in "fix #1", which is to override registerEvent and link up the specific event you want to the specific control you want. Although this is perfectly acceptable, it seems a little... ... ... ugly.

This is somewhat of an edge case, but its a perfectly valid usage - and although there is certainly a way around it (a few ways in fact) i do wonder if something a little more elegant can be created.

Another, less important issue, is having to dispatch the change event in this way, so maybe some type of generic event forwarding / blocking mechanism could fix both this issues in simple (macro based?) way.

Open to suggestions :)

ianharrigan avatar Mar 15 '21 18:03 ianharrigan