tl;dr; I lean toward keeping the Control API as view-agnostic as possible, but 
where view details become essential to the operation of the control, then 
define the Control to always include those specific view details.

============================================================================

Very interesting discussion, I’m enjoying reading through it. The following is 
a bit of background as to where my head was when we were designing this.

In Swing we had a similar concept to the Skins, they were the various UI 
delegates. In theory they allowed you to change the look of a Swing component 
(and they did) — but there were all kinds of “view specific” details that 
leaked into it, and also in the Swing components. With JavaFX, I wanted to go 
down more of a purist route, where the Skin would literally do all the UI 
(view)  and the control would be the controller. OK, the Control was a Node 
because it made more sense conceptually to put a Button into a scene graph than 
to create a Button, associate it with a view, put the View into the scene 
graph, and somewhere stash the controller so you could get it back later. And 
for something like a Button, it works well.

Almost as soon as we’d gone down this road we got into the various problems you 
guys have been discussion around scroll bars and view positions. If you’re 
using the standard UI, then you kind of want to have access to these properties 
so you can get the UX right. However if we add these properties to ListView, 
then it would go down the road of saying “ListView displays a list of items and 
has a scroll bar or at least a scroll position and you have convenience API for 
making certain items visible, etc”.

Another very common complaint was about how to go about knowing the size of a 
cell for a specific list view item. You’d really want an API on ListView that 
said “getCell(item)” and then you got the cell, at least assuming it was 
visible, otherwise since it is virtualized it doesn’t make a whole lot of 
sense. But anyway, if you don’t know whether the skin even does virtualization, 
then how can you define these semantics in a way that is broadly consistent and 
usable?

Basically it comes down to having the API on the controller (Control) and 
restricting what kinds of skins can be created per control or having users cast 
the skin to a concrete type and work with the Skin directly for such things. If 
we were a dynamic language we could be dirty and munge the two together into a 
single thing (dynamic properties), but with its own set of trade-offs (you have 
to know what kind of skin you’re dealing with, and if you get it wrong, your 
app breaks).

As time went on we ended up adding more view-specific functionality to the 
controls (even adding view state via the CSS background API etc!!). I don’t 
know what the right answer is for this design question, but where we were 
heading was a place where you would have different UI controls for different 
“classes” of controls. So a ListView with scrollbars is our ListView, and a 
PagingListView or something would probably be different. They could have a 
common base class if we designed it such, otherwise they would just have those 
similarities that are consistent between the two classes of control.

That is why I find this actually quite appropriate:

> So Skins prevent us from getting visual details of the Control (such
> as scroll position, position of item on screen, ...), because it is
> Skin-specific, but at the same time they fail to customize the look &
> feel, because visual presentation leaks into the Control anyway.


This is the design balance! If you get it wrong, you get exactly what you’re 
saying here.

In the end, I lean toward keeping the Control API as view-agnostic as possible, 
but where view details become essential to the operation of the control, then 
define the Control to always include those specific view details.

Reply via email to