I’ve had a look through the docs and I can’t see anything obvious. Whilst I can get the bounding box of some text drawn on a canvas, my solution is inelegant, to say the least.
I find myself wanting some sort of a canvas::Text::get_bounding_box() method, or maybe even something like Frame::fill_text() returning some metadata about the text that has just been drawn, including the bounding box.
Ultimately, I need the bounding box for two purposes:
So that I can outline the text, as shown with the number 142 in the image below
So that I can calculate when two pieces of text are overlapping each other (and therefore not draw one of the pieces of text.
I suppose, to achieve number 2, I need to be able to tell the size of the bounding box before I draw if with frame::fill_text(), so that I can skip drawing it if there is already some text intersecting that area.
Here’s a little screenshot of a text with no outline (100) and text with an outline drawn by using the bounding box (142):
Ah hah, so the easy way to achieve number two in python is that I made the background of the animated label opaque (but the same colour as the background). This would always draw on top of any previous text, but a limitation in Iced is that text is always drawn on the top layer, no matter what the order of drawing is. The workaround I’ve found is to stack! two canvases on top of each other - a static base image, then a dynamic overlay.
It’s probably more descriptive to show a little animation - I’m implementing these dynamic labels on a chart which accurately show where the mouse cursor is position:
Notice how the animated label on the x axis covers the static labels as it moves over them. Trying to implement this on a single canvas results in the text merging with one another and becoming unreadable. Alternatively, you’d have to track the bounding boxes and selectively skip drawing the static label that is intersecting with the animated label.
Nice workaround! FYI it’s the graphics renderer used by Canvas which makes the assumption that text should always be used as overlay and hence draw it on top
If you implemented your custom widget, you could use the base renderer’s with_layer method to force a layer to be drawn on top of previous layers.
The graphics renderer is an extension of the base renderer, so it’s available there too, just not exposed by the Canvas widget
(details in the above explanation may be slightly off as I’m going off memory typing on my phone)
Interesting, I’ve only just picked up Iced in the last week or so (and achieved more in those few days than I did in months of dynamically-typed Python/Qt/matplotlib), so I haven’t looked into implementing this directly as a widget, although I think that’s what I’ll end up doing eventually. I think that would solve a few problems with splitting this into two separate canvases, such as a lot of duplication of shared state, handling messages logically, etc.
Still, I’m pleased with how flexible the canvas is, and how performant it is, too.
Sounds like you’re having the typical iced experience! It’s ridiculously fast and well designed so you get a lot done very quickly and in a very performant way.
Custom widgets are a great way to solve those issues you mentioned.
When the time comes, you might benefit from reading the qr_code.rs widget source code if you’re drawing geometries (basically creating a canvas-like widget without the need for acanvas::Program)
Then for creating Messages it’s just a matter of adding some builder methods like .on_press, .on_move or whatever you need, to hold onto some boxed closure to create the messages, and you can reference the mouse_area widget for how to do that
It’s a fun little exercise IMHO good luck and happy coding!