001/*
002 * ============================================================================
003 *  Copyright © 2002-2024 by Thomas Thrien.
004 *  All Rights Reserved.
005 * ============================================================================
006 *  Licensed to the public under the agreements of the GNU Lesser General Public
007 *  License, version 3.0 (the "License"). You may obtain a copy of the License at
008 *
009 *       http://www.gnu.org/licenses/lgpl.html
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 *  License for the specific language governing permissions and limitations
015 *  under the License.
016 */
017
018package org.tquadrat.foundation.fx.beans;
019
020import static org.apiguardian.api.API.Status.STABLE;
021import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument;
022
023import java.util.Optional;
024import java.util.function.BiFunction;
025
026import org.apiguardian.api.API;
027import org.tquadrat.foundation.annotation.ClassVersion;
028import org.tquadrat.foundation.fx.internal.FXUserDataBean;
029import javafx.application.Application;
030import javafx.beans.NamedArg;
031import javafx.scene.Node;
032import javafx.scene.Parent;
033import javafx.scene.Scene;
034import javafx.scene.paint.Paint;
035import javafx.stage.Stage;
036
037/**
038 *  <p>{@summary The user data bean for instances of
039 *  {@link Scene}.}</p>
040 *  <p>A JavaFX {@code Scene} instance allows that arbitrary user data can be
041 *  attached to it, using the method
042 *  {@link Scene#setUserData(Object)}.
043 *  This class provides an extensible container for this kind of data.</p>
044 *  <p>Through the methods</p>
045 *  <ul>
046 *      <li>{@link #setProperty(String, Object) setProperty()}</li>
047 *      <li>{@link #getProperty(String) getProperty()}</li>
048 *      <li>{@link #removeProperty(String) removeProperty()}</li>
049 *      <li>{@link #hasProperty(String) hasProperty()}</li>
050 *  </ul>
051 *  <p>it is already possible to store any type of data into the user data
052 *  bean, but it can be easily extended for additional functionality.</p>
053 *
054 *  <h2>{@code createScene()} Factory Methods</h2>
055 *  <p>The methods</p>
056 *  <ul>
057 *      <li>{@link #createScene(Application, Stage, Parent)}</li>
058 *      <li>{@link #createScene(Application, Stage, Parent, Paint)}</li>
059 *      <li>{@link #createScene(Application, Stage, Parent, double, double)}</li>
060 *      <li>{@link #createScene(Application, Stage, Parent, double, double, Paint)}</li>
061 *      <li>{@link #createScene(SceneUserData, Stage, Parent)}</li>
062 *      <li>{@link #createScene(SceneUserData, Stage, Parent, Paint)}</li>
063 *      <li>{@link #createScene(SceneUserData, Stage, Parent, double, double)}</li>
064 *      <li>{@link #createScene(SceneUserData, Stage, Parent, double, double, Paint)}</li>
065 *      <li>{@link #createScene(Application, Stage, Parent, BiFunction)}</li>
066 *      <li>{@link #createScene(Application, Stage, Parent, BiFunction, Paint)}</li>
067 *      <li>{@link #createScene(Application, Stage, Parent, BiFunction, double, double)}</li>
068 *      <li>{@link #createScene(Application, Stage, Parent, BiFunction, double, double, Paint)}</li>
069 *  </ul>
070 *  <p>can be used to create a
071 *  {@link Scene}
072 *  instance together with the user data. Those of the factory methods that do
073 *  <i>not</i> have a {@code supplier} argument will use an instance of
074 *  {@code SceneUserData} (this class); if you want to use an instance of a
075 *  derived class, you have to implement the respective supplier and use one of
076 *  the methods that take it.</p>
077 *
078 *  @param  <A> The class of the JavaFX application.
079 *
080 *  @version $Id: SceneUserData.java 1110 2024-03-04 15:26:06Z tquadrat $
081 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
082 *  @UMLGraph.link
083 *  @since 0.1.0
084 */
085@ClassVersion( sourceVersion = "$Id: SceneUserData.java 1110 2024-03-04 15:26:06Z tquadrat $" )
086@API( status = STABLE, since = "0.1.0" )
087public non-sealed class SceneUserData<A extends Application> extends FXUserDataBean<A>
088{
089        /*------------*\
090    ====** Attributes **=======================================================
091        \*------------*/
092    /**
093     *  The stage for this scene; this can be the same as the primary stage
094     *  in cases where this scene is that for the application's main window.
095     */
096    private final Stage m_Stage;
097
098        /*--------------*\
099    ====** Constructors **=====================================================
100        \*--------------*/
101    /**
102     *  Creates a new {@code SceneUserData} instance.
103     *
104     *  @param  application The reference for the application's main class.
105     *  @param  primaryStage    The reference for the application's primary
106     *      stage.
107     */
108    public SceneUserData( final A application, @SuppressWarnings( "ParameterNameDiffersFromOverriddenParameter" ) final Stage primaryStage )
109    {
110        this( application, primaryStage, primaryStage );
111    }   //  SceneUserData()
112
113    /**
114     *  Creates a new {@code SceneUserData} instance.
115     *
116     *  @param  application The reference for the application's main class.
117     *  @param  primaryStage    The reference for the application's primary
118     *      stage.
119     *  @param  currentStage    The reference to the stage for this scene.
120     */
121    public SceneUserData( final A application, final Stage primaryStage, final Stage currentStage )
122    {
123        super( application, primaryStage );
124
125        m_Stage = requireNonNullArgument( currentStage, "currentStage" );
126    }   //  SceneUserData()
127
128    /**
129     *  Creates a new {@code SceneUserData} instance and copies the references
130     *  for the application, the primary and the current stage from the given
131     *  user data bean (usually that from the application's main scene).
132     *
133     *  @param  userDataBean    The template user data bean.
134     *  @param  currentStage    The reference to the stage for this scene.
135     */
136    @SuppressWarnings( "UseOfConcreteClass" )
137    public SceneUserData( final SceneUserData<? extends A> userDataBean, final Stage currentStage )
138    {
139        this( requireNonNullArgument( userDataBean, "userDataBean" ).getApplication(), userDataBean.getPrimaryStage(), currentStage );
140        userDataBean.getApplicationCSS().ifPresent( this::setApplicationCSS );
141    }   //  SceneUserData()
142
143        /*---------*\
144    ====** Methods **==========================================================
145        \*---------*/
146    /**
147     *  Creates a
148     *  {@link Scene}
149     *  for a specific root
150     *  {@link Node}.
151     *
152     *  @param  <T> The type of the application's main class.
153     *  @param  application The reference for the application's main class.
154     *  @param  primaryStage    The reference for the application's primary
155     *      stage.
156     *  @param  root    The root node of the scene graph.
157     *  @return The new scene instance.
158     */
159    @API( status = STABLE, since = "0.1.0" )
160    public static final <T extends Application> Scene createScene( @NamedArg( "application" ) final T application, @NamedArg( "primaryStage" ) final Stage primaryStage, @NamedArg( "root" ) final Parent root )
161    {
162        final var retValue = createScene( application, primaryStage, root, SceneUserData::new );
163
164        //---* Done *----------------------------------------------------------
165        return retValue;
166    }   //  createScene()
167
168    /**
169     *  Creates a
170     *  {@link Scene}
171     *  for a specific root
172     *  {@link Node}.
173     *
174     *  @param  <T> The type of the application's main class.
175     *  @param  application The reference for the application's main class.
176     *  @param  primaryStage    The reference for the application's primary
177     *      stage.
178     *  @param  root    The root node of the scene graph.
179     *  @param  supplier    The supplier for the {@code SceneUserData}
180     *      instance.
181     *  @return The new scene instance.
182     */
183    @API( status = STABLE, since = "0.1.0" )
184    public static final <T extends Application> Scene createScene( @NamedArg( "application" ) final T application, @NamedArg( "primaryStage" ) final Stage primaryStage, @NamedArg( "root" ) final Parent root, @NamedArg( "supplier") final BiFunction<T, ? super Stage, ? extends SceneUserData<T>> supplier )
185    {
186        final var userData = requireNonNullArgument( supplier, "supplier" ).apply( requireNonNullArgument( application, "application" ), requireNonNullArgument( primaryStage, "primaryStage" ) );
187        final var retValue = new Scene( requireNonNullArgument( root, "root" ) );
188        primaryStage.setScene( retValue );
189        retValue.setUserData( userData );
190
191        //---* Done *----------------------------------------------------------
192        return retValue;
193    }   //  createScene()
194
195    /**
196     *  Creates a
197     *  {@link Scene}
198     *  for a specific root
199     *  {@link Node}
200     *  with a specific size.
201     *
202     *  @param  <T> The type of the application's main class.
203     *  @param  application The reference for the application's main class.
204     *  @param  primaryStage    The reference for the application's primary
205     *      stage.
206     *  @param  root    The root node of the scene graph.
207     *  @param  width   The width of the scene.
208     *  @param  height  The height of the scene.
209     *  @return The new scene instance.
210     */
211    @API( status = STABLE, since = "0.1.0" )
212    public static final <T extends Application> Scene createScene( @NamedArg( "application" ) final T application, @NamedArg( "primaryStage" ) final Stage primaryStage, @NamedArg( "root" ) final Parent root, @NamedArg( "width" ) final double width, @NamedArg( "height" ) final double height )
213    {
214        final var retValue = createScene( application, primaryStage, root, SceneUserData::new, width, height );
215
216        //---* Done *----------------------------------------------------------
217        return retValue;
218    }   //  createScene()
219
220    /**
221     *  Creates a
222     *  {@link Scene}
223     *  for a specific root
224     *  {@link Node}
225     *  with a specific size.
226     *
227     *  @param  <T> The type of the application's main class.
228     *  @param  application The reference for the application's main class.
229     *  @param  primaryStage    The reference for the application's primary
230     *      stage.
231     *  @param  root    The root node of the scene graph.
232     *  @param  supplier    The supplier for the {@code SceneUserData}
233     *      instance.
234     *  @param  width   The width of the scene.
235     *  @param  height  The height of the scene.
236     *  @return The new scene instance.
237     */
238    @SuppressWarnings( "MethodWithTooManyParameters" )
239    @API( status = STABLE, since = "0.1.0" )
240    public static final <T extends Application> Scene createScene( @NamedArg( "application" ) final T application, @NamedArg( "primaryStage" ) final Stage primaryStage, @NamedArg( "root" ) final Parent root, @NamedArg( "supplier") final BiFunction<T, ? super Stage, ? extends SceneUserData<T>> supplier, @NamedArg( "width" ) final double width, @NamedArg( "height" ) final double height )
241    {
242        final var userData = requireNonNullArgument( supplier, "supplier" ).apply( requireNonNullArgument( application, "application" ), requireNonNullArgument( primaryStage, "primaryStage" ) );
243        final var retValue = new Scene( requireNonNullArgument( root, "root" ), width, height );
244        primaryStage.setScene( retValue );
245        retValue.setUserData( userData );
246
247        //---* Done *----------------------------------------------------------
248        return retValue;
249    }   //  createScene()
250
251    /**
252     *  Creates a
253     *  {@link Scene}
254     *  for a specific root
255     *  {@link Node}
256     *  with a fill.
257     *
258     *  @param  <T> The type of the application's main class.
259     *  @param  application The reference for the application's main class.
260     *  @param  primaryStage    The reference for the application's primary
261     *      stage.
262     *  @param  root    The root node of the scene graph.
263     *  @param  fill    The fill.
264     *  @return The new scene instance.
265     */
266    @API( status = STABLE, since = "0.1.0" )
267    public static final <T extends Application> Scene createScene( @NamedArg( "application" ) final T application, @NamedArg( "primaryStage" ) final Stage primaryStage, @NamedArg( "root" ) final Parent root, @NamedArg( value = "fill", defaultValue = "WHITE" ) final Paint fill )
268    {
269        final var retValue = createScene( application, primaryStage, root, SceneUserData::new, fill );
270
271        //---* Done *----------------------------------------------------------
272        return retValue;
273    }   //  createScene()
274
275    /**
276     *  Creates a
277     *  {@link Scene}
278     *  for a specific root
279     *  {@link Node}
280     *  with a fill.
281     *
282     *  @param  <T> The type of the application's main class.
283     *  @param  application The reference for the application's main class.
284     *  @param  primaryStage    The reference for the application's primary
285     *      stage.
286     *  @param  root    The root node of the scene graph.
287     *  @param  supplier    The supplier for the {@code SceneUserData}
288     *      instance.
289     *  @param  fill    The fill.
290     *  @return The new scene instance.
291     */
292    @API( status = STABLE, since = "0.1.0" )
293    public static final <T extends Application> Scene createScene( @NamedArg( "application" ) final T application, @NamedArg( "primaryStage" ) final Stage primaryStage, @NamedArg( "root" ) final Parent root, @NamedArg( "supplier") final BiFunction<T, ? super Stage, ? extends SceneUserData<T>> supplier, @NamedArg( value = "fill", defaultValue = "WHITE" ) final Paint fill )
294    {
295        final var userData = requireNonNullArgument( supplier, "supplier" ).apply( requireNonNullArgument( application, "application" ), requireNonNullArgument( primaryStage, "primaryStage" ) );
296        final var retValue = new Scene( requireNonNullArgument( root, "root" ), requireNonNullArgument( fill, "fill" ) );
297        primaryStage.setScene( retValue );
298        retValue.setUserData( userData );
299
300        //---* Done *----------------------------------------------------------
301        return retValue;
302    }   //  createScene()
303
304    /**
305     *  Creates a
306     *  {@link Scene}
307     *  for a specific root
308     *  {@link Node}
309     *  with a specific size and fill.
310     *
311     *  @param  <T> The type of the application's main class.
312     *  @param  application The reference for the application's main class.
313     *  @param  primaryStage    The reference for the application's primary
314     *      stage.
315     *  @param  root    The root node of the scene graph.
316     *  @param  width   The width of the scene.
317     *  @param  height  The height of the scene.
318     *  @param  fill    The fill.
319     *  @return The new scene instance.
320     */
321    @SuppressWarnings( "MethodWithTooManyParameters" )
322    @API( status = STABLE, since = "0.1.0" )
323    public static final <T extends Application> Scene createScene( @NamedArg( "application" ) final T application, @NamedArg( "primaryStage" ) final Stage primaryStage, @NamedArg( "root" ) final Parent root, @NamedArg( "width" ) final double width, @NamedArg( "height" ) final double height, @NamedArg( value = "fill", defaultValue = "WHITE" ) final Paint fill )
324    {
325        final var retValue = createScene( application, primaryStage, root, SceneUserData::new, width, height, fill );
326
327        //---* Done *----------------------------------------------------------
328        return retValue;
329    }   //  createScene()
330
331    /**
332     *  Creates a
333     *  {@link Scene}
334     *  for a specific root
335     *  {@link Node}
336     *  with a specific size and fill.
337     *
338     *  @param  <T> The type of the application's main class.
339     *  @param  application The reference for the application's main class.
340     *  @param  primaryStage    The reference for the application's primary
341     *      stage.
342     *  @param  root    The root node of the scene graph.
343     *  @param  supplier    The supplier for the {@code SceneUserData}
344     *      instance.
345     *  @param  width   The width of the scene.
346     *  @param  height  The height of the scene.
347     *  @param  fill    The fill.
348     *  @return The new scene instance.
349     */
350    @SuppressWarnings( "MethodWithTooManyParameters" )
351    @API( status = STABLE, since = "0.1.0" )
352    public static final <T extends Application> Scene createScene( @NamedArg( "application" ) final T application, @NamedArg( "primaryStage" ) final Stage primaryStage, @NamedArg( "root" ) final Parent root, @NamedArg( "supplier") final BiFunction<T, ? super Stage, ? extends SceneUserData<T>> supplier, @NamedArg( "width" ) final double width, @NamedArg( "height" ) final double height, @NamedArg( value = "fill", defaultValue = "WHITE" ) final Paint fill )
353    {
354        final var userData = requireNonNullArgument( supplier, "supplier" ).apply( requireNonNullArgument( application, "application" ), requireNonNullArgument( primaryStage, "primaryStage" ) );
355        final var retValue = new Scene( requireNonNullArgument( root, "root" ), width, height, requireNonNullArgument( fill, "fill" ) );
356        primaryStage.setScene( retValue );
357        retValue.setUserData( userData );
358
359        //---* Done *----------------------------------------------------------
360        return retValue;
361    }   //  createScene()
362
363    /**
364     *  Creates a
365     *  {@link Scene}
366     *  for a specific root
367     *  {@link Node}.
368     *
369     *  @param  <T> The type of the application's main class.
370     *  @param  templateDataBean    The user data from the application's main
371     *      scene.
372     *  @param  currentStage    The stage for the current scene.
373     *  @param  root    The root node of the scene graph.
374     *  @return The new scene instance.
375     */
376    @SuppressWarnings( "UseOfConcreteClass" )
377    @API( status = STABLE, since = "0.1.0" )
378    public static final <T extends Application> Scene createScene( @NamedArg( "templateDataBean" ) final SceneUserData<T> templateDataBean, @NamedArg( "currentStage" ) final Stage currentStage, @NamedArg( "root" ) final Parent root )
379    {
380        final var newUserDataBean = new SceneUserData<>( templateDataBean, currentStage );
381        final var retValue = new Scene( requireNonNullArgument( root, "root" ) );
382        currentStage.setScene( retValue );
383        retValue.setUserData( newUserDataBean );
384        newUserDataBean.getApplicationCSS().ifPresent( u -> retValue.getStylesheets().add( u.toExternalForm() ) );
385
386        //---* Done *----------------------------------------------------------
387        return retValue;
388    }   //  createScene()
389
390    /**
391     *  Creates a
392     *  {@link Scene}
393     *  for a specific root
394     *  {@link Node}
395     *  with a specific size.
396     *
397     *  @param  <T> The type of the application's main class.
398     *  @param  templateDataBean    The user data from the application's main
399     *      scene.
400     *  @param  currentStage    The stage for the current scene.
401     *  @param  root    The root node of the scene graph.
402     *  @param  width   The width of the scene.
403     *  @param  height  The height of the scene.
404     *  @return The new scene instance.
405     */
406    @SuppressWarnings( "UseOfConcreteClass" )
407    @API( status = STABLE, since = "0.1.0" )
408    public static final <T extends Application> Scene createScene( @NamedArg( "templateDataBean" ) final SceneUserData<T> templateDataBean, @NamedArg( "currentStage" ) final Stage currentStage, @NamedArg( "root" ) final Parent root, @NamedArg( "width" ) final double width, @NamedArg( "height" ) final double height )
409    {
410        final var newUserDataBean = new SceneUserData<>( templateDataBean, currentStage );
411        final var retValue = new Scene( requireNonNullArgument( root, "root" ), width, height );
412        currentStage.setScene( retValue );
413        retValue.setUserData( newUserDataBean );
414        newUserDataBean.getApplicationCSS().ifPresent( u -> retValue.getStylesheets().add( u.toExternalForm() ) );
415
416        //---* Done *----------------------------------------------------------
417        return retValue;
418    }   //  createScene()
419
420    /**
421     *  Creates a
422     *  {@link Scene}
423     *  for a specific root
424     *  {@link Node}
425     *  with a fill.
426     *
427     *  @param  <T> The type of the application's main class.
428     *  @param  templateDataBean    The user data from the application's main scene.
429     *  @param  currentStage    The stage for the current scene.
430     *  @param  root    The root node of the scene graph.
431     *  @param  fill    The fill.
432     *  @return The new scene instance.
433     */
434    @SuppressWarnings( "UseOfConcreteClass" )
435    @API( status = STABLE, since = "0.1.0" )
436    public static final <T extends Application> Scene createScene( @NamedArg( "templateDataBean" ) final SceneUserData<T> templateDataBean, @NamedArg( "currentStage" ) final Stage currentStage, @NamedArg( "root" ) final Parent root, @NamedArg( value = "fill", defaultValue = "WHITE" ) final Paint fill )
437    {
438        final var newUserDataBean = new SceneUserData<>( templateDataBean, currentStage );
439        final var retValue = new Scene( requireNonNullArgument( root, "root" ), requireNonNullArgument( fill, "fill" ) );
440        currentStage.setScene( retValue );
441        retValue.setUserData( newUserDataBean );
442        newUserDataBean.getApplicationCSS().ifPresent( u -> retValue.getStylesheets().add( u.toExternalForm() ) );
443
444        //---* Done *----------------------------------------------------------
445        return retValue;
446    }   //  createScene()
447
448    /**
449     *  Creates a
450     *  {@link Scene}
451     *  for a specific root
452     *  {@link Node}
453     *  with a specific size and fill.
454     *
455     *  @param  <T> The type of the application's main class.
456     *  @param  templateDataBean    The user data from the application's main scene.
457     *  @param  currentStage    The stage for the current scene.
458     *  @param  root    The root node of the scene graph.
459     *  @param  width   The width of the scene.
460     *  @param  height  The height of the scene.
461     *  @param  fill    The fill.
462     *  @return The new scene instance.
463     */
464    @SuppressWarnings( {"UseOfConcreteClass", "MethodWithTooManyParameters"} )
465    @API( status = STABLE, since = "0.1.0" )
466    public static final <T extends Application> Scene createScene( @NamedArg( "templateDataBean" ) final SceneUserData<T> templateDataBean, @NamedArg( "currentStage" ) final Stage currentStage, @NamedArg( "root" ) final Parent root, @NamedArg( "width" ) final double width, @NamedArg( "height" ) final double height, @NamedArg( value = "fill", defaultValue = "WHITE" ) final Paint fill )
467    {
468        final var newUserDataBean = new SceneUserData<>( templateDataBean, currentStage );
469        final var retValue = new Scene( requireNonNullArgument( root, "root" ), width, height, requireNonNullArgument( fill, "fill" ) );
470        currentStage.setScene( retValue );
471        retValue.setUserData( newUserDataBean );
472        newUserDataBean.getApplicationCSS().ifPresent( u -> retValue.getStylesheets().add( u.toExternalForm() ) );
473
474        //---* Done *----------------------------------------------------------
475        return retValue;
476    }   //  createScene()
477
478    /**
479     *  Returns the stage for the current scene. If the current scene is the
480     *  application's main scene, the returned stage is the primary stage, so
481     *  that in this case
482     *  {@link #getPrimaryStage()}
483     *  will return the same value as this method.
484     *
485     *  @return The stage for the current scene.
486     */
487    public final Stage getStage() { return m_Stage; }
488
489    /**
490     *  Retrieves the user data from the given instance of
491     *  {@link Scene}.
492     *
493     *  @param  <T> The type of the application's main class.
494     *  @param  scene   The scene.
495     *  @param  applicationClass    The type of the application's main class.
496     *  @return An instance of
497     *      {@link Optional}
498     *      that holds the user data instance.
499     */
500    public static final <T extends Application> Optional<SceneUserData<T>> retrieveUserData( final Scene scene, final Class<T> applicationClass )
501    {
502        Optional<SceneUserData<T>> retValue = Optional.empty();
503        final var userData = requireNonNullArgument( scene, "scene" ).getUserData();
504        //noinspection rawtypes
505        if( userData instanceof final SceneUserData userDataBean )
506        {
507            final var app = userDataBean.getApplication();
508            if( applicationClass.isInstance( app ) )
509            {
510                @SuppressWarnings( "unchecked" )
511                final var data = (SceneUserData<T>) userData;
512                retValue = Optional.of( data );
513            }
514        }
515
516        //---* Done *----------------------------------------------------------
517        return retValue;
518    }   //  retrieveUserData()
519}
520//  class SceneUserData
521
522/*
523 *  End of File
524 */