Re: [External] : Aw: Proposal: Add Skin.install() method
Kevin Rushforth
kevin.rushforth at oracle.com
Fri Jul 22 20:41:58 UTC 2022
I don't know if you really meant Error, as in java.lang.Error, but it
would need to be a subclass of RuntimeException.
IllegalArgumentException seems the natural choice (a case could possibly
be made for IllegalStateException). Throwing an Error is not the right
thing for a library to do in response to an application passing in an
illegal or unexpected argument to a method or constructor. It is for
truly exceptional things that a programmer cannot anticipate (like
running out of memory).
-- Kevin
On 7/22/2022 12:37 PM, Andy Goryachev wrote:
>
> I would rather throw an Error in Skinnable.setSkin() when mismatch is
> detected. This is a design error that should be caught early in
> development rather than a run time exception.
>
> -andy
>
> *From: *openjfx-dev <openjfx-dev-retn at openjdk.org> on behalf of Kevin
> Rushforth <kevin.rushforth at oracle.com>
> *Date: *Friday, 2022/07/22 at 12:33
> *To: *openjfx-dev at openjdk.org <openjfx-dev at openjdk.org>
> *Subject: *Re: [External] : Aw: Proposal: Add Skin.install() method
>
> I would not be in favor of adding a no-arg constructor to SkinBase,
> for the reasons Andy gave. Additionally, there would be no way to
> avoid braking the contract of Skin::getSkinnable which says:
>
> "This value will only ever go from a non-null to null value when the
> Skin is removed from the Skinnable, and only as a consequence of a
> call to dispose()."
>
>
> At the very minimum, we should explain in Skin javadoc that
> creating a skin for one control and setting it in the other is a
> no-no. Or, perhaps we should explicitly check for this condition
> in setSkin().
>
>
> I agree completely. At a minimum this enhancement should change the
> docs for setSkin to say that a skin created for one control should not
> (must not?) be used in another control. And unless there is a
> legitimate use case I haven't thought of, I think we could consider an
> explicit check, and either throw an Exception (this seems the best
> choice, unless there are compatibility concerns), or else log a
> warning and treat it as a no-op.
>
> -- Kevin
>
> On 7/22/2022 9:13 AM, Andy Goryachev wrote:
>
> You do bring a good point! I don't know the rationale behind
> passing control pointer to the Skin constructor.
>
> I think Swing got it right, clearly separating
>
> 1. instantiation (using either a no-arg constructor, or any other
> constructor that does not require component pointer)
> 2. configuration (optional step, possibly widely separated in
> time and space)
> 3. uninstallation of the old skin
> 4. installation of the new skin
>
> What you are proposing - moving to a default constructor makes the
> most sense. It comes with a high price though - everyone with a
> custom skin implementation would need to change their code.
>
> At the very minimum, we should explain in Skin javadoc that
> creating a skin for one control and setting it in the other is a
> no-no. Or, perhaps we should explicitly check for this condition
> in setSkin().
>
> Thank you
>
> -andy
>
> *From: *Marius Hanl <mariushanl at web.de> <mailto:mariushanl at web.de>
> *Date: *Friday, 2022/07/22 at 05:06
> *To: *openjfx-dev at openjdk.org <openjfx-dev at openjdk.org>
> <mailto:openjfx-dev at openjdk.org>, Andy Goryachev
> <andy.goryachev at oracle.com> <mailto:andy.goryachev at oracle.com>
> *Subject: *[External] : Aw: Proposal: Add Skin.install() method
>
> I had a similar idea in the past and like the idea.
> Ideally, setting/switching a skin is a one step process. Currently
> you can construct a skin for a control and set it after to a
> different control.
>
> Your approach sounds good, if you can set a skin by creating a new
> skin (with a default constructor) and then the setSkin() method
> will actually trigger the install process on the control (this),
> this will work and solve the problem above. But for backward
> compatibilty we still need to keep the skin constructor with the
> control as parameter and think about deprecating it.
>
> -- Marius
>
> Am 20.07.22, 23:40 schrieb Andy Goryachev
> <andy.goryachev at oracle.com> <mailto:andy.goryachev at oracle.com>:
>
> Hi,
>
> I'd like to propose an API change in Skin interface (details
> below). Your feedback will be greatly appreciated!
>
> Thank you,
>
> -andy
>
> Summary
>
> -------
>
> Introduce a new Skin.install() method with an empty default
> implementation. Modify Control.setSkin(Skin) implementation
> to invoke install() on the new skin after the old skin has
> been removed with dispose().
>
> Problem
>
> -------
>
> Presently, switching skins is a two-step process: first, a new
> skin is constructed against the target Control instance, and
> is attached (i.s. listeners added, child nodes added) to that
> instance in the constructor. Then, Control.setSkin() is
> invoked with a new skin - and inside, the old skin is detached
> via its dispose() method.
>
> This creates two problems:
>
> 1. if the new skin instance is discarded before setSkin(), it
> remains attached, leaving the control in a weird state with
> two skins attached, causing memory leaks and performance
> degradation.
>
> 2. if, in addition to adding listeners and child nodes, the
> skin sets a property, such as an event listener, or a handler,
> it overwrites the current value irreversibly. As a result,
> either the old skin would not be able to cleanly remove
> itself, or the new skin would not be able to set the new
> values, as it does not know whether it should overwrite or
> keep a handler installed earlier (possibly by design).
> Unsurprisingly, this also might cause memory leaks.
>
> We can see the damage caused by looking at JDK-8241364
> <https://bugs.openjdk.org/browse/JDK-8241364> ☂/Cleanup skin
> implementations to allow switching/, which refers a number of
> bugs:
>
> JDK-8245145 Spinner: throws IllegalArgumentException when
> replacing skin
>
> JDK-8245303 InputMap: memory leak due to incomplete cleanup on
> remove mapping
>
> JDK-8268877 TextInputControlSkin: incorrect inputMethod event
> handler after switching skin
>
> JDK-8236840 Memory leak when switching ButtonSkin
>
> JDK-8240506 TextFieldSkin/Behavior: misbehavior on switching skin
>
> JDK-8242621 TabPane: Memory leak when switching skin
>
> JDK-8244657 ChoiceBox/ToolBarSkin: misbehavior on switching skin
>
> JDK-8245282 Button/Combo Behavior: memory leak on dispose
>
> JDK-8246195 ListViewSkin/Behavior: misbehavior on switching skin
>
> JDK-8246202 ChoiceBoxSkin: misbehavior on switching skin, part 2
>
> JDK-8246745 ListCell/Skin: misbehavior on switching skin
>
> JDK-8247576 Labeled/SkinBase: misbehavior on switching skin
>
> JDK-8253634 TreeCell/Skin: misbehavior on switching skin
>
> JDK-8256821 TreeViewSkin/Behavior: misbehavior on switching skin
>
> JDK-8269081 Tree/ListViewSkin: must remove flow on dispose
>
> JDK-8273071 SeparatorSkin: must remove child on dispose
>
> JDK-8274061 Tree-/TableRowSkin: misbehavior on switching skin
>
> JDK-8244419 TextAreaSkin: throws UnsupportedOperation on dispose
>
> JDK-8244531 Tests: add support to identify recurring issues
> with controls et al
>
> Solution
>
> --------
>
> This problem does not exist in e.g. Swing because the steps of
> instantiation, uninstalling the old ComponentUI ("skin"), and
> installing a new skin are cleanly separated. ComponentUI
> constructor does not alter the component itself,
> ComponentUI.uninstallUI(JComponent) cleanly removes the old
> skin, ComponentUI.installUI(JComponent) installs the new
> skin. We should follow the same model in javafx.
>
> Specifically, I'd like to propose the following changes:
>
> 1. Add Skin.install() with a default no-op implementation.
>
> 2. Clarify skin creation-attachment-detachment sequence in
> Skin and Skin.install() javadoc
>
> 3. Modify Control.setSkin(Skin) method (== invalidate
> listener in skin property) to call oldSkin.dispose() followed
> by newSkin.install()
>
> 4. Many existing skins that do not set properties in the
> corresponding control may remain unchanged. The skins that
> do, such as TextInputControlSkin (JDK-8268877), must be
> refactored to utilize the new install() method. I think the
> refactoring would simply move all the code that accesses its
> control instance away from the constructor to install().
>
> Impact Analysis
>
> -------------
>
> The changes should be fairly trivial. Only a subset of skins
> needs to be refactored, and the refactoring itself is trivial.
>
> The new API is backwards compatible with the existing code,
> the customer-developed skins can remain unchanged (thanks to
> default implementation). In case where customers could
> benefit from the new API, the change is trivial.
>
> The change will require CSR as it modifies a public API.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20220722/745028f3/attachment-0001.htm>
More information about the openjfx-dev
mailing list