Hi, I’d like to discuss approaches to API design in the context of https://bugreports.qt.io/browse/QTBUG-47850 (making Quick Controls dialogs’ standard buttons’ enabled properly state accessible).
It seems, that the desired properties of an API implementing the above would be: possibility to declaratively manipulate enabled-ness of the buttons lack of risks brought by “signal races” (usually happens when imperative and declarative code is mixed) optionally, possibility to tweak more than just the “enabled” property of the buttons (at least the API could allow for such extension in the future). Below are some approaches coming to mind (after a discussion we had with colleagues): 1. A getter method on Dialog, more or less duplicating that of the QDialog. Pros are the API (and the patch implementing it) being trivial. Cons are necessity to call the getter only after the button is created (by otherwise declarative code) and complexities of button lifetime tracking. 2. A set of applyEnabled, helpEnabled, … properties on the Dialog. Pros is being declarative and API simplicity, cons is the obvious verbosity (especially, if more properties are exposed this way later). 3. An enabledButtons (disabledButtons? disabledStandardButtons? enabledStandardButtons??) property containing an ORed set of button roles (just like the standardButtons property). Pros are things being declarative, and uniform, cons -- that crafting a binding expression for more than a couple buttons may get ugly (and listening / responding to changes through the single QML signal handler may require having a signal parameter matching the old / previous state of the ORed enum combo -- which is strange; note: something close is discussed by https://bugreports.qt.io/browse/QTBUG-40868?focusedCommentId=253265) 4. An auxiliary object can be used to make the buttons accessible declaratively: Dialog { StandardButtonProxy { id: okProxy role: StandardButton.Ok Binding { target: okProxy.button // here it is property: “enabled” value: <some binding expression> } } } Pros of this approach would be its simplicity (on both API and implementation sides), and relative safety (the “button” property would be null when the button is not there, incl. temporarily -- i.e. during button model reorganizations, etc.), cons -- that it would be merely a means to expose standard buttons “the QML way”, not addressing the fact that exposing control instances from underneath may not be the best practice by itself (and especially in QML, where a smart user may try to save the reference, thus confusing the GC). 5. A special ButtonState object type serving as “declarative proxy” for the button’s properties, i.e.: Dialog { <...> ButtonState { role: StandardButton.Ok enabled: <some binding expression> } } Pros of this approach would be “proper” declarativity, and ability to easily extend the exposed property set (adding possibility to override other properties other than “enabled”), cons would be limiting further API changes in this area to being based on this one-way “reflect my properties” approach (and making some bindings track the state of the button by default would complicate things beyond reasonable). 6. Finally, Dialog could accept (optional) delegates for the buttons created, allowing for arbitrary customizations, i.e.: Dialog { <...> StandardButtonDelegate { //name arguably could be better role: StandardButton.Apply // could be “roles” here even StandardButton { // a Button, but with default bindings for “text”, etc. enabled: <some binding expression> } } StandardButtonDelegate { //name arguably could be better role: StandardButton.Apply // could be “roles” here even Rectangle { // a very custom “button” <...> signal clicked // or a warning emitted by Dialog if absent enabled: <some binding expression> } } } The pros of this approach would be the possibility to customize the buttons (standard or not) freely, including styling, overriding text (when / if necessary) etc., while being fully declarative. This way, the Dialog only does button management (i.e. platform-specific layout, creation and destruction), but the aspect of button’s behavior is completely separated (providing for a nice separation of concerns), and there’re no API bottlenecks like proxy property bags (#5 above) and so on. An obvious con would be ease of breaking layouts with geometry bindings / anchors specified for the button (unless specifically addressed (and unset) by Dialog’s logic). Overall, approaches #5 and #6 seem to attract more sympathy among those who discussed the API decisions in question here internally. What would you guys say (most importantly, Qt Quick Controls patch reviewers, but everyone else interested as well)?
_______________________________________________ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development