Hard Problems™ With Custom Controls

I’m looking at a new project that will require a LOT of custom controls - basically the entire UI needs to be very custom. So I’ll be building new buttons, labels, checkboxes, etc out of canvases.

In principle this should work fine, and does work in most cases. But I need to have a window with a custom background image, with custom controls layered on top of it. This is causing rectangles to appear around all the custom controls. That won’t work for me.

Here’s an example mock-up:

The “untitled” label there is being drawn in a subclass of canvas by me manually. I don’t want the light grey box around it - just the word alone.

No amount of fiddling with double buffering, transparent, etc gets the thing to actually be transparent.

How is this done? The only idea I have at this point is to create a Picture behind the scenes, and draw to it’s graphics context with a mask, then draw that picture to the canvas. Thought I’d see who had already cracked this nut before I tried that, though.

Which platforms? If you’re targeting OS X only, this should be a piece of cake. If you need to support Windows and Linux, you’ll need to ditch transparency entirely and fake it.

Kimball I had a similar problem today actually. I had to use the “eye dropper” from idraw to detect the RGB Color of my window backcolor. I then used that RGB color on the canvas.backcolor so they matched and looked seamless. Your window is gradient so that may be just a bit harder, but doable.

Your example looks like OS X. I didn’t have any trouble there with a quick example:

  • Use a label with background set to transparent :slight_smile: or
  • Use canvas with a prepared picture that has a transparent background.

Here is an example. Untitled black is made with a label, Untitled red is made with a canvas and a transparent picture.

@Thom McGrath - I do have to target win/mac, so looks like I’ll have to fake it.

@Mike Cotrone - It’s actually nastier than that. These controls will live on windows with arbitrary images for backgrounds. Sometimes there may be a gradient behind it, other times it may be a picture of the eiffel tower, a goldfish, or my breakfast. The point is if I’m going to fake it, about the only way I can think of is to somehow tell the label what the background image was that was drawn on the window, and then draw the same portion of that background image that the label covers such that it meshes seamlessly with the window behind it.

I’ve done this technique in the past with other apps for custom toolbar buttons etc, and it works well but is not a great generalized solution, as you are forced to wire up the UI widgets to at least know what picture was drawn behind them and at what coordinates.

@Paul Lefebvre - Thanks for the quick mockup… is your blue just a background color, or is it an actual image? Also, I need to target windows.

@Michel Bujardet - your example looks interesting. How does it do on windows?

I’ve gotten some good progress on this, and have a solution that is working fine on the mac, but not on windows due to pretty horrible flicker. In my paint event for my canvas: (ignore magic numbers etc for positioning)

[code] dim p as new Picture(g.width, g.height, 32)
dim m as Picture = p.Mask

m.graphics.ForeColor = &cFFFFFF
m.graphics.FillRect(0, 0, m.graphics.width, m.graphics.height)

m.graphics.TextFont = “Open Sans”
m.graphics.TextSize = 32
m.graphics.ForeColor = &c000000
m.graphics.drawString(text, 4, g.height / 2 + 10)

p.mask = m

p.graphics.textFont = “Open Sans”
p.Graphics.textSize = 32
p.Graphics.drawString(text, 4, g.height / 2 + 10)

g.drawPicture(p, 0, 0)
[/code]

This works, but flickers badly on windows. If I enable double buffering for the canvas, then the white background comes back.

@ Kimball

If your entire UI needs to be very custom, why don’t you try the ‘custom layout’ road as well ?

By that I mean having only one Canvas at all covering your entire Window, then create ‘virtual’ Controls (not derived from Canvas that will be managed by a ‘Layout’ system (derived from Canvas). This way you can make your all UI much faster (and zero flicker) and have much more flexibility. Things such as transparency will be implicit as you will mostly only draw once what you need.

(that’s what I’m working on actually, but on OpenGLSurface)

Cheers,
Guy.

Grab a copy of the rectangle behind the window/control, and set this image as a background of the floating window/control, maybe?

I would prefer to convince the client to rethink the UI specs than resort to this kind of thing.

@Guy Rabiller - an interesting proposition. At design time, however, it would be pretty impossible to see how things are laid out.

@Louis Desjardins - this is essentially what I’m looking at having to do - draw the part of the background image again in the custom control to mesh perfectly with the window it sits on.

Is there a reliable way to simply ask a window for the pixels at certain coordinates?

It is easy to create a little tool to design you UI, you can even use XojoScript if you don’t want to create one with ‘widgets’. Pretty easy to do.

(yet some UI don’t require ‘design time’ as the layout system is meant to be managed by the user (fluid layout with splittable or dockable views etc…)

I’m not trying to ‘sell’ you this idea though : -)

But if you often have to create completely custom UI, I think this is an option not to be underestimated.

Cheers,
Guy.

Just one idea…

Create a window subclass like WndWithBackground, create a method that returns the background image BackgroundImage, then have your control ask the window for its background image through that method. Once you have it, you can take the slice you need pretty easily. If you’ve stored the image in a window property, this should be speedy enough, and it will be pretty portable.

@ Kimball

If you take the ‘copy background to control canvas’ road, rest assured this is exactly what some commercial custom UI toolkits do, So don’t be ashame to do that, you will be fine.

The drawback is that this leads to horribly slow user interfaces when it comes to resize a Window.
(in short, avoid resizable user interface with this approach :wink: )

Cheers,
Guy.

I would go the other route, personally. I would make the canvases be the special case. At runtime, they turn themselves invisible and you pass graphics clips from the main window into their paint events, so they all draw to the same graphics object.

Tim’s idea is one I’d never considered here… just to clarify: When in the paint event of a custom control, rather than drawing onto that control’s graphics context, grab a hold of the graphics of the containing window and draw there instead?

To do this, I’d need to expose the graphics object of the parent window, but that is a possibly cleaner solution than anything else I’ve considered.

Great ideas, everyone!

@Michel Bujardet - does your example flicker on Windows at all when either the window or widget paint event fires?

I have used the canvases backdrop. No need to paint :wink:

The Untitled black label will probably flicker less than the canvas one, I guess.