I used to believe that downcasting was always possible to avoid with a "proper" design. This just isn't the case though. A proper design very often needs to have sub-objects that implement new, and not just different behavior. Too often advocates of "proper" design will tell you to move the new behavior up the stack of abstraction to places where it doesn't belong. Not always, but if you keep trying to make sure all your classes can be used from the most abstract point this is very often where things end up going and it's just fugly.
One great way to deal with downcasting in a centralized manner is by using the Visitor pattern. There are several forms of visitor though, some require downcasting, some do not. The acyclic visitor, the one that does require downcasting, is easier to work with and is, in my experience, more powerful.
Another visitor that I've not attempted to work with claims to meet the same flexibility of acyclic visitor with the speed of the standard visitor; it's called "cooperative visitor". It still casts, it just does so in a quicker manner with it's own lookup table. The reason I have not tried the cooperative visitor is that I've not found a way to make it work on multiple higherarchies...but I've not spent a lot of time on it either because I've stuck myself (in my current project) with acyclic.
The real cool thing about the cooperative visitor is return types. However, I use my visitors to visit entire blocks of objects and do things with them. I have trouble envisioning how a return would work in these cases.
The standard visitor downcasts also it just does so through the virtual call mechanism, which is faster and sometimes safer than an explicit cast. The thing I don't like about this visitor is that if you need to visit WidgetX in the Widget higherarchy then you also have to implement visit() functionality for WidgetY and WidgetZ even though you don't care about them. With large and/or wide higherarchies this can be a PITA. Other options do not require this.
There is also a "higherarchal visitor". It knows when to quit.
If you're not inclined to use a visitor though and wish to just cast then you might consider using the boost::polymorphic_downcast function. It has the safety and warning mechanisms of dynamic cast with asserts in debug builds, and the speed of static cast in a release. It may not be necessary though. Sometimes you just know that you're casting right.
The important thing that you need to think about and what you want to avoid, is breaking the LSP. If you have a whole lot of code with "if (widget->type() == type1) { downcast...} else if (widget->type() == type2)..." then adding new widget types is a big issue that affects a lot of code in a bad way. Your new widget won't really be a widget because all your clients are too intimate with your higherarchy and don't know about it. The visitor pattern does not get rid of this issue but it does centralize, which is very important when you've got a bad smell, and it often makes it simpler to deal with on top of it.