Canvas component?

What needs to go into my assets/main.xml to create & house this hx.widgets.Panel? That is:

<vbox style="padding: 5px;" width="100%" height="100%">
    <button id="button1" text="Draw things!" />
    <slider id="slider1" />
    <the-hx-widgets-panel id="panel1" />
    <label text="behold the above" />
</vbox>

Then, in my Main.main app.ready function I’d be able to grab it:

panel1 = mainView.findComponent("panel1", Panel);

so I could then use that code you’ve provided above to call panel.bind(...), right?

(That is, I wouldn’t have to manually create the new Panel myself, since I think it gets created via the mainView = ComponentMacros.buildComponent("assets/main.xml");.)

OK, so “the-hx-widgets-panel” in the xml would need to be a custom component. Also, keep in mind that “Panel” is an hxWidgets type, and nothing to do with haxeui. You could grab the .window property from a haxeui component (which in the underlying hxWidgets control).

So:

<vbox style="padding: 5px;" width="100%" height="100%">
    <button id="button1" text="Draw things!" />
    <slider id="slider1" />
    <box id="panel1" />
    <label text="behold the above" />
</vbox>
var box = mainView.findComponent("panel1", Box); // this is a haxeui component
var panel = cast(box.window, Panel); // the haxeui component is a wrapper for the wx window

Thanks, Ian. I can’t get that graphics panel to display. Here’s what I have so far:

main.xml:

<vbox style="padding: 5px;" width="100%" height="100%">
    <button id="button1" text="Draw things!" />
    <slider id="slider1" />
    <box id="box1" />
    <label text="Graphics!" />
</vbox>

Main.hx: https://gist.github.com/uvtc/c1300a6dfaf4b4eaf1aa931f72b4fb5b

The window opens and displays the other widgets, but no panel present.

Does the box (box1) need a size maybe?

Ok, so i tried to make it work with a size and it actually still didnt work, there were some issues in hxWidgets. So, they are fixed now (you’ll need git versions ofc, though im making a release very soon)

This is what the code looks like (at the moment you have to manually call update & refresh on the underlying wxWindow, but if this all moves to a new haxeui Canvas component, the ofc, that will happen automatically):

<vbox id="main" style="padding: 5px;" width="100%" height="100%">
    <button id="button1" text="Draw things!" />
    <slider id="slider1" />
    <box id="box1" width="100%" height="100%" />
    <label text="Graphics!" />
</vbox>

image

class Main {
    private static var _max:Float = 50;
    
    public static function main() {
        var app = new HaxeUIApp();
        app.ready(function() {
            var mainView:Component = ComponentMacros.buildComponent("assets/main.xml");
            app.addComponent(mainView);

            var box1 = mainView.findComponent("box1", Box);
            var panel1:Panel = cast(box1.window, Panel);
            
            panel1.bind(EventType.PAINT, function(e) {
                var dc = new PaintDC(panel1);
                dc.background = StockBrushes.BRUSH_BLACK;
                dc.clear();
                
                dc.pen = new Pen(0xff0000, 3);
                dc.brush = new Brush(0x880000);
                dc.drawRoundedRectangle(10, 30, Std.int(_max * 2), Std.int(_max), 10);

                dc.pen = new Pen(0xffffff, 3);
                dc.drawLine(10, 120, Std.int(10 + _max), Std.int(120 + _max));

                dc.textForeground = 0xFF00FF;
                dc.drawText("Hello, GraphicsContext!", 10, 10);
                
                dc.gradientFillLinear(new Rect(10, 200, 100, 50), 0xFF8888, 0x88FFFF);
            });
            
            var slider1 = mainView.findComponent("slider1", HorizontalSlider);
            slider1.min = 50;
            slider1.max = 150;
            slider1.onChange = function(e) {
                _max = slider1.pos;
                panel1.refresh();
                panel1.update();
            }
            
            app.start();
        });
    }
}
1 Like

Sorry for the delay in getting back to you.

This works for me! Thanks so much!!

I’m going to see if I can separate some of that out into handlers and a drawing routine and then post about my results.

1 Like

Great, yeah, see if it works for you the “manual” way and if there are any functions missing that you need on the wxDC - ill think about wrapping all this up (and other backends) in a new haxeui canvas system in the mean time :slight_smile:

Hi, Ian. A few questions:

  1. Can you comment on the connection between how hxWidgets handles events vs how HaxeUI does it? Above, for the Panel, we have panel1.bind(EventType.PAINT, ...) to set up the hx.widgets.Panel’s event handler, but we do things like slider1.onChange = handleSlider1 to set up the haxe.ui.components.Slider event…

  2. Somewhere along the way I mixed up using a hx.widgets.PaintDC and a hx.widgets.GraphicsContext. I see you’re now switched to using the PaintDC. What’s the difference between these two, and which should I use?

  3. Hm. I noticed that I’m not getting that “Graphics!” Label text at the bottom, underneath the Panel. It’s not displaying that text there for me, like it does in your screenshot. Any idea why it’s missing for me? My main.xml is:

<vbox style="padding: 5px;" width="100%" height="100%">
    <button id="button1" text="Draw things!" />
    <slider id="slider1" />
    <box id="box1" width="100%" height="100%" />
    <label text="Graphics!" />
</vbox>

and my Main.hx is:

Screenshot:

Thanks!

1:
So, haxeui-core has its own event system, it lives “above” any backend event system. But basically what happens is that when you register an event in haxeui-core (registerEvent) it sets up a listener, etc that lives inside the component, it then calls “mapEvent” on the backend (via ComponentImpl), which sets up a backend / framework specific event. Lets say that event then fires, then the backend (via ComponentImpl) lets haxeui-core know, which then fires the appropriate user code that was registered via registerEvent. So, as with most things the event system inside haxeui-core is essentially a wrapper (of sorts) around any event system that exists in the backend.

The reason you are using the underlying wxWidges event EventType.PAINT is because there is NO mapping for a paint event in haxeui-core (nor will there ever be as its not relevant - its not how haxeui core handles “drawing”). So really the way this drawing in haxeui works above is purley a way to use the underlying backend (which certainly is useful, but does mean you app is no longer cross framework), in reality you are subverting haxeui and using the underlying hxWidgets framework.

At some point this is exactly why a new Canvas component will be included in haxeui, and that version, inside haxeui-hxwidgets will use the EventType.PAINT to achieve its implementation.

So simply put: Assuming you are using the haxeui-hxwidgets backend:

var button = new Button(); // haxeui-button
button.onClick = function(e) { ... }

This is using “pure” haxeui, it will create a native button and will map wxWidgets EventType.Button to a callback in the haxeui-core Button (sort of), which when dispatched will call your function in onClick. Now consider:

var button = new Button(); // haxeui button
var wxButton = cast(button.window, hx.widgets.Button); // this is the underlying native wxWidgets button
wxButton.bind(EventType.Button, function(e) {});

Functionally this will do exactly the same, but you are subverting haxeui-core now, and tapping into the hxWidgets backend, which means a) onClick wont fire on the haxeui button wrapper now (as it will never know its been clicked) and b) this will only work with haxeui-hxwidgets as you are using the underlying native window (.window).

Note, that basically (in a slightly more long winded way) haxeui-hxwidgets does the 2nd code snippet when you do the first.

2:
So actually, the DC was the correct way to go in first place (i think) but there were bugs (oversights really) in the hxWidgets lib that were creating these DCs correctly. And i think (assume) i hit those problems and worked around them by just using the GraphicsContext, which actually hid the issue rather than fixing them. That said, i should have been able to create a GraphicsContext from a DeviceContext, but when i tried i got some VERY strange results (the drawing would be on my screen outside of the app - very strange). Ive added this to my, seemingly infinite, list to investigate - but for now DC’s should suffice and i think are the advisable way to do things anyway in wxWidgets

3:
Hmmm, thats strange, i defo do get it, if you remove all the drawing stuff does it come back? Is it a specific call that makes it dissapear? Is it the actual binding of the paint event that breaks it? If you move it above the panel does it work? Certainly weird that one component seems to be affecting another.

Phew, that was long! :smiley:

1 Like

Thanks so much for the detailed reply, Ian. Sorry to take so long to get back to it.

You wrote:

I’ve never used registerEvent. What class is this a method of? Can you give me an example of using it? I’ve so far only ever used myWidget.onClick = someHandlerFn;, myWidget2.onChange = someHandlerFn2;.

In your example code above, you wrote: var wxButton = cast(button.window, hx.widgets.Button);. I looked around in the Button API docs, as well as in InteractiveComponent and Component, but couldn’t find that window field. Where is it?

So registerEvent is the haxeui method for its event handlers, weirdly, thats another function that the docgen seems to have skipped :confused:

But basically its along the lines of button.registerEvent(MouseEvent.CLICK, function(e) {}), in fact, onClickis just a shortcut that does exactly this, actually, there is one difference:

button1.registerEvent(MouseEvent.CLICK, function(e) { trace(1); });
button1.registerEvent(MouseEvent.CLICK, function(e) { trace(2); });

button2.onClick =  function(e) { trace(1) };
button2.onClick =  function(e) { trace(2) };

the first button (button1) will trace “1” and “2” when clicked, the second button (button2) will just trace “2” - that is to say, that with registerEvent you can register multiple listeners of the same type, with the shortcuts (onClick) you overwrite a single event handler.

So the .window field is specific to haxeui-hxwidgets and therefore isnt in the docs, im not 100% sure it should be either as its not really supposed to be used directly, i mean, its good you can and thats useful, but i wonder if putting it in the docs is an invitation to use - ill think about it.

I didnt get around to docgen last weekend, hopefully this weekend! :slight_smile:

2 Likes