Wow, that was some dream!
I have to sleep on this for a bit. :) It seems like a very complete
solution. I'm wondering if there is some way to simplify it for a first
implementation, like don't have code paths for each browser. If one
browser doesn't support it, handle it yourself. Then we could add the
browser bit as an optimization. Unless it is just as easy to add it up
front while we are in the code.
- Jeanne
Simon Lessard wrote:
Hello all,
I'm early today because I had a nightmarish sleep about this issue. There
was a huge blue 'e' hovering above the ground and everything under it was
gray, dull and boring... All movies were starring Steven Seagal and
the only
singers left were Brian Adam and Céline Dion, it was HORRIBLE !
Anyway, after waking, I could not find sleep again and I thought about
a way
to handle the nasty :hover. It's a small modification from my first
suggestion about it in an attempt to make it less messy. Tell me what you
think of it.
So far the solution for most pseudo-classes tends toward a
Map<ComponentType, List<PseudoClass>> or simply a map of intercepted
pseudo-classes for a given component. How that list will be
implemented is
yet to be determined though. Anyway, that's not the issue. I thought
about
maintaining two collections. The first would be the map and the second
would
be a list of conditionally intercepted pseudo-classes effectively
including
all valid CSS pseudo-classes. During CSS generation, the selector would
first be read and transformed in the following form:
<base>(:<interceptedState>)*(::<part>(:<interceptedState>)*)*(:<conditionallyInterceptedState>)*(:<passThroughState>)*
Then the conditionallyInterceptedState would be checked against the agent
for which the CSS is getting rendered to see if that agent lets it pass
through. If so, it would be moved to the passThroughState section of the
selector else the state would be included in the selector for purpose of
renaming. For every conditionallyInterceptedState, the renderer could
have a
method returning a <JSEvent, String> tuple specifying on what event
and with
what script the pseudo-class can be emulated, but the method would return
null if the current agent can already handle it. Let make an example
using
":hover".
We have two components:
- component1 that intercepts :hover
- component2 the let :hover pass though
Since :hover is a valid CSS pseudo-class, it can be found inside the
conditionally intercepted pseudo-classes list.
We have 2 skinning keys inside the skin's CSS:
- af|component1:hover
- af|component2:hover
For af|component1:hover, it's simple. Since it,s part of the intercepted
list of component1, it's intercepted.
For af|component2:hover, since :hover might pass though, the CSS
generation
class checks if :hover is suppoerted for the current agent. If it's,
:hover
is simply written to the CSS file and is not placed in the final selector
list (since af|component2 will). However, if it is not supported on the
current agent, :hover is considered intercepted and the whole
af|component2:hover become a new selector in the selector list (and will
thus have its own short style class name). During rendering, in the
writeJS
method(s) a getHoverScript("af|component2:hover") method would be
called. If
the specified selector cannot be found in the list, it means that the
pseudo-class passed through and the agent can handle it or it means that
af|component2:hover was just not defined at all in the skin CSS, then the
method should return null. On the other hand, if the selector is
found, it
means the user's agent cannot handle :hover and the method should then
return <"onmouseover", "this.class=' " + selector.getStyleClass() + "
';">
tuple. The return value is then simply interpreted by the writeJS
method to
add the good event and script to the generated markup. This pattern is, I
believe, decently clean, scalable and reusable without being too hard to
implements nor maintain.
Regards,
~ Simon