It would be really neat if we could do this
void Component::setOpaque (bool shouldBeOpaque, const BorderSize& transparentBorderAmounts = BorderSize());
if shouldBeOpaque is true then the opaque portion is equal to the component bounds, minus the border. So a component could have an opaque body, but transparent sides.
This is great for the case when you have a control that includes its own drop shadow or three dimensional shading, because it can get most of the benefit of setOpaque(true), instead of none (since currently, you have to make such a control non-opaque).
It’d be much neater to just put an opaque central component inside (or in front of) a bigger, non-transparent one. (That’s how the DropShadower class currently works).
It would definitely be easier in the sense that Juce already supports it. But then these Component need to be tied together so they both move as a unit. If you want to show/hide the control then you need to refer to the frame, but if you want to manipulate the contents of such a control you would have to obtain a pointer to the child Component* inside.
The other blemish is then you have the drawing code for the frame located elsewhere. It can’t re-use calculations. For example, a rounded corner rectangle would have to produce the Path twice, once for the partially transparent frame and then again for the interior.
By the way, I’m talking about shadows and hilites that are 1 or 2 pixels thick.
Honestly, it’s an awful idea! You’re suggesting that every component should carry around an extra BorderSize member, and that all the repainting code would need to be massively more complicated to take this into account… And it’d achieve something that you can already do very cleanly by simply putting an opaque “content” component in front of a transparent “border” component!
And separating the code that paints a border from the code that paints its content is not a blemish, it’s a good thing!
A job that I’ve been meaning to do sometime is to genericise (is that a real word?) the DropShadower code, into something that’s basically a component decorator, which will create and manage components that float behind or near your component, and this could be used for shadows or any other kind of stuff. But there’d be no need to bodge the component base class to achieve this - there are plenty of more elegant ways to do it!
I’ve accepted that this border is going to be in another function but I’m trying to at least keep it in the same class. Keep in mind that my border overlaps both the opaque portion of the control as well as a small area just outside it. So I would have to use paintOverChildren() so that I can get the thing to look smooth.
I’m having a hard time doing this. I have a generic control that takes another class as a parameter to control its appearance. I want to put the opaque portion into its own “content” Component as you suggested, and have that call the appearance class.
But this presents a problem, the parent, which is the Control, now doesn’t receive mouse enter/exit messages, the scroll wheel messages are going to the content component, etc… I know about Component::setInterceptsMouseClicks() and I put that in there and it worked fine but there is no similar functionality for mouse enter and exit, mouse scroll wheel.
So I’m trying to do it the way that you suggested but I’m running into problems. I wish there was a way to make a Component completely “transparent” to all user inputs, not just clicks. This includes mouse moves, scroll wheel, and possibly other things that I can’t think of (key presses?).
Maybe if all your messages are going to your content component, then that’s the component that should be dealing with them, rather than trying to pretend it’s not there?
I know it contains the word “clicks” but it actually does disable all mouse events by making hitTest() return false. So no wheel or enter/exit/move events can reach the component.
I separated out the interface logic, drawing, and model (i.e. full model/view/controller) just like you recommended in another one of your posts, and the consequence of making things look the way they need to look is that the “holder” Component that draws the opaque portion of the control, doesn’t get to be the lucky guy who processes the messages.
Hmm…it was a bug on my end! So once again, it turns out you already thought of everything. Thanks!
WOW WTF…I was just going to ask for a way to paint without clipping for Component that don’t need it and ho ho ho what do I find, Component::setPaintingIsUnclipped() Jules you’re a genius!
Okay, thanks to some Juce changes and some fresh thinking inspired by Jules, I solved the problem described in the original post.
I now have a handy class that allows a transparent Component to have a smaller opaque sibling component sit behind it in the z-order and follow it around, handling all cases (component getting deleted, reparented, visibility change, z-order change). This solution allows me to keep all the drawing in a single paint() function while still having controls whose border can be transparent. Here’s an example
void AwesomeButton::paint (Graphics& g)
Rectangle<int> bounds = getLocalBounds();
Rectangle<int> r = bounds;
p.addRoundedRectangle ( r.getX()+frameSize/2, r.getY()+frameSize,2
r.getWidth()-frameSize, r.getHeight()-frameSize, cornerRadius);
g.setColour (Colour::fromRGBA (0, 0, 0, 128)); // 50% black
g.strokePath (p, frameSize);
Even though the button has rounded corners and a transparent frame, the interior of the rounded rectangle is in fact fully opaque, and I can still get the optimized drawing for the solid portion while keeping a transparent border of 3 pixels, using the technique of having an opaque “blocker” component sit behind my button in the z-order. This fully addresses the use-case in the original post.
I totally agree that hacking up the Component would have been a poor choice. However, having a separate component that draws the frame or drop shadow, was an unsatisfying solution for many reasons (my buttons are more complicated than this trivialized example). With the change to detecting z-order changes (http://rawmaterialsoftware.com/viewtopic.php?f=2&t=6420) it is possible to implement this without error.
VFLib contains a simple easy to use class for optimizing the drawing of a Component that is mostly opaque, but has transparency at the borders. It’s called TransparentBorder and it implements the concepts I described in my posts.