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; 019 020import static org.apiguardian.api.API.Status.STABLE; 021import static org.tquadrat.foundation.lang.Objects.isNull; 022import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 023 024import org.apiguardian.api.API; 025import org.tquadrat.foundation.annotation.ClassVersion; 026import org.tquadrat.foundation.annotation.UtilityClass; 027import org.tquadrat.foundation.exception.PrivateConstructorForStaticClassCalledError; 028import javafx.event.ActionEvent; 029import javafx.scene.control.Button; 030import javafx.scene.control.MenuItem; 031import javafx.stage.Window; 032 033/** 034 * Some useful utility function for the work with JavaFX. 035 * 036 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 037 * @version $Id: FXUtils.java 1134 2024-05-20 16:53:16Z tquadrat $ 038 * @since 0.4.2 039 * 040 * @UMLGraph.link 041 */ 042@ClassVersion( sourceVersion = "$Id: FXUtils.java 1134 2024-05-20 16:53:16Z tquadrat $" ) 043@API( status = STABLE, since = "0.4.2" ) 044@UtilityClass 045public final class FXUtils 046{ 047 /*---------------*\ 048 ====** Inner Classes **==================================================== 049 \*---------------*/ 050 051 /*-----------*\ 052 ====** Constants **======================================================== 053 \*-----------*/ 054 055 /*------------*\ 056 ====** Attributes **======================================================= 057 \*------------*/ 058 059 /*------------------------*\ 060 ====** Static Initialisations **=========================================== 061 \*------------------------*/ 062 063 /*--------------*\ 064 ====** Constructors **===================================================== 065 \*--------------*/ 066 /** 067 * No instance allowed for this class! 068 */ 069 private FXUtils() { throw new PrivateConstructorForStaticClassCalledError( FXUtils.class ); } 070 071 /*---------*\ 072 ====** Methods **========================================================== 073 \*---------*/ 074 /** 075 * <p>{@summary Clamps the given value to be strictly between the 076 * {@code min} and {@code max} values.}</p> 077 * <p>Basically, this method does the same as 078 * {@link Math#clamp(double, double, double)}, 079 * only the sequence of the arguments is different.</p> 080 * 081 * @param min The lower border. 082 * @param value The value. 083 * @param max The upper border. 084 * @return The value if it is greater than {@code min} and less than 085 * {@code max}, {@code min}, when it is less than {@code min}, or 086 * {@code max} when it is greater than that. 087 * @throws IllegalArgumentException {@code min} is greater than {@code max}. 088 * 089 * @since 0.4.6 090 */ 091 @API( status = STABLE, since = "0.4.6" ) 092 public static final double clamp( final double min, final double value, final double max ) throws IllegalArgumentException 093 { 094 final var retValue = Math.clamp( value, min, max ); 095 096 //---* Done *---------------------------------------------------------- 097 return retValue; 098 } // clamp() 099 100 /** 101 * <p>{@summary Returns either {@code less} or {@code more} depending on 102 * which one is closer to {@code value}.} If {@code value} is perfectly 103 * between them, then either may be returned.</p> 104 * 105 * @param less The lower value. 106 * @param value The reference value. 107 * @param more The upper value. 108 * @return The value that is closer to the reference. 109 * 110 * @since 0.4.6 111 */ 112 @API( status = STABLE, since = "0.4.6" ) 113 public static final double nearest( final double less, final double value, final double more ) 114 { 115 final var lessDiff = value - less; 116 final var moreDiff = more - value; 117 final var retValue = lessDiff < moreDiff ? less : more; 118 119 //---* Done *---------------------------------------------------------- 120 return retValue; 121 } // nearest() 122 123 /** 124 * Retrieves the window owning the 125 * {@link Button} 126 * that fired the given 127 * {@link ActionEvent}. 128 * 129 * @param event The action event. 130 * @return The owning window. 131 * @throws IllegalArgumentException The event was not fired by a 132 * {@code Button}. 133 */ 134 public static final Window retrieveOwner( final ActionEvent event ) throws IllegalArgumentException 135 { 136 final var retValue = switch( requireNonNullArgument( event, "event" ).getSource() ) 137 { 138 case null -> throw new IllegalArgumentException( "Event source is null" ); 139 140 /* 141 * When the triggering event originates from a button, that button 142 * is part of a scene (perhaps defined by an FXML file or created 143 * from scratch). So we just walk up the scenes to the root, and 144 * from there we take the Window that owns that button. That is 145 * quite often a Stage. 146 */ 147 case final Button button -> button.getScene() 148 .getRoot() 149 .getScene() 150 .getWindow(); 151 152 /* 153 * When the event is triggered from a menu item, this is either 154 * part of a menu or a context menu. In both cases it has the same 155 * path to the window that owns it. 156 */ 157 case final MenuItem menuItem -> 158 { 159 var resultItem = menuItem.getParentPopup(); 160 var currentItem = menuItem; 161 while( isNull( resultItem ) ) 162 { 163 currentItem = currentItem.getParentMenu(); 164 resultItem = currentItem.getParentPopup(); 165 } 166 yield resultItem.getOwnerWindow(); 167 } 168 169 default -> throw new IllegalArgumentException( "Inappropriate event source" ); 170 }; 171 172 //---* Done *---------------------------------------------------------- 173 return retValue; 174 } // retrieveOwner() 175} 176// class FXUtils 177 178/* 179 * End of File 180 */