jfxtras.scene.layout.responsivepane.ResponsivePane Maven / Gradle / Ivy
The newest version!
/**
* Copyright (c) 2011-2021, JFXtras
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the organization nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JFXTRAS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jfxtras.scene.layout.responsivepane;
import java.util.List;
import java.util.TreeMap;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Screen;
import javafx.stage.Window;
/**
* = ResponsivePane
*
* This layout chooses the best fitting layout and stylesheet for a given stage size.
*
* ResponsivePane is loosely based on responsive design as advocated by website designs, and implemented in for example the Twitter Bootstrap project.
* But there is a twist in the logic; responsive design assumes a given width and unlimited vertical space with the use of a scrollbar.
* For websites this is ok, but applications do not have unlimited vertical space.
* Take for example a webbrowser; it contains a webview with a scrollbar, but it does not run inside a scrollpane itself.
* So for applications there is no such thing as unlimited vertical space!
* It has to use scalable controls, like lists, tables ort webview, to dynamically fill its horizontal and vertical space, just like we are used to in Swing and JavaFX for ages.
* But given large screen size differences, from 4 inch phones to 32 inch desktops, it is ridiculous to think that a single layout can adapt to all sizes: on a phone you may choose to use a TabbedPane with less controls visible, and on desktop you go all out with a MigPane and a number of gimmicks.
* ResponsiveLayout allows you to define reusable nodes and have specific screen size related layouts, using the reusable nodes.
* It will then use what layout fits best.
*
* A typical use of ResponsivePane would look like this:
* [source,java]
* --
@Override
public void start(Stage primaryStage) throws Exception {
// create pane with reusable nodes
ResponsivePane lResponsivePane = new ResponsivePane();
lResponsivePane.addReusableNode("CalendarPicker", new CalendarPicker());
lResponsivePane.addReusableNode("TreeView", new TreeView());
lResponsivePane.addReusableNode("TableView", new TableView());
lResponsivePane.addReusableNode("save", new Button("save"));
lResponsivePane.addReusableNode("saveAndTomorrow", new Button("saveAndTomorrow"));
lResponsivePane.addReusableNode("-", new Button("-"));
lResponsivePane.addReusableNode("+", new Button("+"));
lResponsivePane.addReusableNode("Logo", new Button("Logo"));
lResponsivePane.addReusableNode("version", new Label("v1.0"));
// define custom device
lResponsivePane.setDeviceSize("PHABLET", Diagonal.inch(9.0));
// define layouts
lResponsivePane.addLayout(Diagonal.inch(3.5), createPhoneLayout());
lResponsivePane.addLayout(Diagonal.inch(12.0), createDesktopLayout());
// define css
lResponsivePane.addSceneStylesheet(Diagonal.inch(4.0), getClass().getResource("phone.css").toExternalForm());
lResponsivePane.addSceneStylesheet(Diagonal.inch(6.0), getClass().getResource("tablet.css").toExternalForm());
lResponsivePane.addSceneStylesheet(Diagonal.inch(12.0), getClass().getResource("desktop.css").toExternalForm());
}
private Node createDesktopLayout() {
MigPane migPane = new MigPane();
migPane.add(new Ref("Logo"), new CC());
migPane.add(new Ref("version"), new CC().spanX(2).alignX("right").alignY("top").wrap());
migPane.add(new Ref("CalendarPicker"), new CC().alignY("top"));
migPane.add(new Ref("TreeView"), new CC().grow().pushX().spanY(3).wrap());
...
return migPane;
}
private Node createPhoneLayout() {
TabPane tabPane = new TabPane();
tabPane.getTabs().add(createTab("Date", "Calendar", new Ref("CalendarPicker")));
tabPane.getTabs().add(createTab("Tree", "Projectboom", new Ref("TreeView")));
tabPane.getTabs().add(createTab("Calc", "Totalen", new Ref("TreeView")));
...
return tabPane;
}
* --
*
* The exact implementation of createPhoneLayout() and createDesktopLayout() is not relevant, except that they use "Ref" nodes to include the reusable nodes.
*
* == Reusable nodes:
* Reusable nodes are identified by their id, this is done for ease of use in FXML.
* They can then be used in the different layouts via a "new Ref()".
* Be aware that you cannot reuse Refs, they are place holders and each layout needs a Ref instance on its own.
*
*
* == Stylesheets:
* Like layouts it is also possible to activate stylesheets based on the size of the stage.
* A stylesheet can be assigned to the scene (applicable for everything on the scene) or to the layout (applicable for the underlying node).
* Responsive design assigned classes to the nodes, based on the size.
* Test have shown that this approach works ok when running the application on a desktop computer, with enough CPU power (GUI Garage's implementation does this), but on mobile these changes have a sever performance impact.
* So ResponsivePane does not assign classes, but instead it replaces whole CSS files; for the CSS engine this is a single change and it needs to rerender only once.
* In those CSS files you can assign behavior to the CSS classes, or not.
*
* [source,java]
* --
// scene stylesheet
responsivePane.addSceneStylesheet(Diagonal.inch(1.0), getClass().getResource("phone.css").toExternalForm());
// pane stylesheet
responsivePane.addMyStylesheet(Diagonal.inch(3.0), getClass().getResource("tablet.css").toExternalForm());
* --
*
* There can only be one scene or layout stylesheet active at a given time.
*
* == Size:
* Determining the size is an interesting topic.
* Responsive design only looks at the width in pixel, but it is already explained that ResponsivePane does not assume unlimited vertical space, so the height must also be taking into account.
* Therefore size is expressed as a diagonal instead of the width.
* However, responsive design specifies the width in pixels, but 1000 pixels on a 100 ppi screen is something completely different on a 350 ppi screen; the first is 10 inches in real life, the second not even 3 inch!
* ResponsivePane therefore (per default) expresses the size in inches (or cm), as we are used to do when talking about devices; a 4 inch phone, a 27 inch monitor.
* But as a compatibility behavior it also is possible to the width; it will be at runtime converted to diagonal using the actual height of the layout.
*
* There also is a third option to set the size; ResponsivePane has some default diagonals for typical devices in the Device class, but you can also defined custom devices.
*
* So sizes for layout and stylesheets can be defined like:
* [source,java]
* --
responsivePane.addLayout(Diagonal.inch(3.5), ...);
responsivePane.addLayout(Width.inch(3.0), ...);
responsivePane.addLayout(Device.PHONE, ...);
responsivePane.setDeviceSize("PHABLET", Diagonal.inch(9.0));
responsivePane.addLayout(DeviceSize.of("PHABLET"), ...);
* --
*
* == FXML
* ResponsivePane can of course also be used from FXML.
* Interesting is the how the width-based size is specified.
*
* [source,xml]
* --
* --
*/
public class ResponsivePane extends StackPane {
// TODO: should stylesheets also support orientation?
// TODO: should we refactor to have a single 'SizeBasedResource' beneath both Layout and Stylesheet?
// ==========================================================================================================================================================================================================================================
// CONSTRUCTORS
/**
*
*/
public ResponsivePane() {
// just for easy reading
SINGULARITY_LAYOUT.getRoot().setId("ResponsivePane.emptyLayout");
// default device sizes
setDeviceSize(Device.PHONE, Diagonal.inch(3.5));
setDeviceSize(Device.TABLET, Diagonal.inch(7.0));
setDeviceSize(Device.DESKTOP, Diagonal.inch(13.0));
// react to changes in the available layouts and stylesheets
layouts.addListener( (javafx.collections.ListChangeListener.Change extends Layout> c) -> {
if (getTrace()) log(">>> requestLayout from changes in layouts, size=" + layouts.size());
requestLayout();
});
sceneStylesheets.addListener( (javafx.collections.ListChangeListener.Change extends Stylesheet> c) -> {
if (getTrace()) log(">>> requestLayout from changes in scene stylesheets, size=" + sceneStylesheets.size());
requestLayout();
});
myStylesheets.addListener( (javafx.collections.ListChangeListener.Change extends Stylesheet> c) -> {
if (getTrace()) log(">>> requestLayout from changes in my stylesheets, size=" + myStylesheets.size());
requestLayout();
});
}
// ==========================================================================================================================================================================================================================================
// PROPERTIES
/** Id */
public ResponsivePane withId(String v) {
setId(v);
return this;
}
/** Debug: show rendering hints (for Ref) and prints out changes to the layout on the console */
public ObjectProperty debugProperty() { return debugProperty; }
final private SimpleObjectProperty debugProperty = new SimpleObjectProperty(this, "debug", Boolean.FALSE);
public Boolean getDebug() { return debugProperty.getValue(); }
public void setDebug(Boolean value) { debugProperty.setValue(value); }
public ResponsivePane withDebug(Boolean value) { setDebug(value); return this; }
/** Trace: like debug, plus show calculations determining if changes are needed on the console */
public ObjectProperty traceProperty() { return traceProperty; }
final private SimpleObjectProperty traceProperty = new SimpleObjectProperty(this, "trace", Boolean.FALSE);
public Boolean getTrace() { return traceProperty.getValue(); }
public void setTrace(Boolean value) { traceProperty.setValue(value); }
public ResponsivePane withTrace(Boolean value) { setTrace(value); return this; }
private void log(String s) {
System.out.println(s);
}
// -----------------------------------------------------------------------------------------------
// Reusable nodes
/** refs */
public ObservableList getReusableNodes() {
return reusableNodes;
}
final private ObservableList reusableNodes = FXCollections.observableArrayList();
/**
*
* @param id
* @param node
* @return
*/
public Node addReusableNode(String id, Node node) {
node.setId(id);
getReusableNodes().add(node);
return node;
}
/**
*
* @param node
* @return
*/
public Node addReusableNode(Node node) {
if (node.getId() == null || node.getId().trim().length() == 0) {
throw new IllegalArgumentException("A reusable node must have an id");
}
getReusableNodes().add(node);
return node;
}
/**
*
* @param refId
* @return
*/
Node findResuableNode(String refId) {
for (Node lNode : reusableNodes) {
if (refId.equals(lNode.getId())) {
return lNode;
}
}
System.err.println("Could not find reference " + refId);
return null;
}
// -----------------------------------------------------------------------------------------------
// LAYOUTS
/** layouts */
public ObservableList getLayouts() {
return layouts;
}
final private ObservableList layouts = FXCollections.observableArrayList();
/**
* Convenience method for addLayout(Size, Node)
*/
public void addLayout(Device device, Node root) {
addLayout(device.toString(), root);
}
/**
* Convenience method for addLayout(Size, Node)
*/
public void addLayout(String device, Node root) {
addLayout(deviceSizes.get(device), root);
}
/**
* Convenience method for getLayouts().add(new Layout(sizeAtLeast, root))
*/
public void addLayout(Size sizeAtLeast, Node root) {
layouts.add(new Layout(sizeAtLeast, root));
}
/**
* Convenience method for addLayout(Size, Orientation, Node)
*/
public void addLayout(Device device, Orientation orientation, Node root) {
addLayout(device.toString(), orientation, root);
}
/**
* Convenience method for addLayout(Size, Orientation, Node)
*/
public void addLayout(String device, Orientation orientation, Node root) {
addLayout(deviceSizes.get(device), orientation, root);
}
/**
* Convenience method for getLayouts().add(new Layout(sizeAtLeast, orientation, root))
*/
public void addLayout(Size sizeAtLeast, Orientation orientation, Node root) {
layouts.add(new Layout(sizeAtLeast, orientation, root));
}
/** ActiveLayout */
public ObjectProperty activeLayoutProperty() { return activeLayoutProperty; }
final private SimpleObjectProperty activeLayoutProperty = new SimpleObjectProperty(this, "activeLayout", null);
public Layout getActiveLayout() { return activeLayoutProperty.getValue(); }
public void setActiveLayout(Layout value) { activeLayoutProperty.setValue(value); }
public ResponsivePane withActiveLayout(Layout value) { setActiveLayout(value); return this; }
// -----------------------------------------------------------------------------------------------
// SceneStylesheets
/** sceneStylesheets */
public ObservableList getSceneStylesheets() {
return sceneStylesheets;
}
final private ObservableList sceneStylesheets = FXCollections.observableArrayList();
/**
* Convenience method for addSceneStylesheet(Size sizeAtLeast, String file)
*/
public void addSceneStylesheet(Device device, String file) {
addSceneStylesheet(device.toString(), file);
}
/**
* Convenience method for addSceneStylesheet(Size sizeAtLeast, String file)
*/
public void addSceneStylesheet(String device, String file) {
addSceneStylesheet(deviceSizes.get(device), file);
}
/**
* Convenience method for getSceneStylesheets().add(new Stylesheet(sizeAtLeast, file));
*/
public void addSceneStylesheet(Size sizeAtLeast, String file) {
sceneStylesheets.add(new Stylesheet(sizeAtLeast, file));
}
/** ActiveSceneStylesheet */
public ObjectProperty activeSceneStylesheetProperty() { return activeSceneStylesheetProperty; }
final private SimpleObjectProperty activeSceneStylesheetProperty = new SimpleObjectProperty(this, "activeSceneStylesheet", null);
public Stylesheet getActiveSceneStylesheet() { return activeSceneStylesheetProperty.getValue(); }
public void setActiveSceneStylesheet(Stylesheet value) { activeSceneStylesheetProperty.setValue(value); }
public ResponsivePane withActiveSceneStylesheet(Stylesheet value) { setActiveSceneStylesheet(value); return this; }
// -----------------------------------------------------------------------------------------------
// MyStylesheets
/** myStylesheets */
public ObservableList getMyStylesheets() {
return myStylesheets;
}
final private ObservableList myStylesheets = FXCollections.observableArrayList();
/**
* Convenience method for addMyStylesheet(Size sizeAtLeast, String file)
*/
public void addMyStylesheet(Device device, String file) {
addMyStylesheet(device.toString(), file);
}
/**
* Convenience method for addMyStylesheet(Size sizeAtLeast, String file)
*/
public void addMyStylesheet(String device, String file) {
addMyStylesheet(deviceSizes.get(device), file);
}
/**
* Convenience method for getMyStylesheets().add(new Stylesheet(sizeAtLeast, file));
*/
public void addMyStylesheet(Size sizeAtLeast, String file) {
myStylesheets.add(new Stylesheet(sizeAtLeast, file));
}
/** ActiveMyStylesheet */
public ObjectProperty activeMyStylesheetProperty() { return activeMyStylesheetProperty; }
final private SimpleObjectProperty activeMyStylesheetProperty = new SimpleObjectProperty(this, "activeMyStylesheet", null);
public Stylesheet getActiveMyStylesheet() { return activeMyStylesheetProperty.getValue(); }
public void setActiveMyStylesheet(Stylesheet value) { activeMyStylesheetProperty.setValue(value); }
public ResponsivePane withActiveMyStylesheet(Stylesheet value) { setActiveMyStylesheet(value); return this; }
// ==========================================================================================================================================================================================================================================
// DEVICE
public final ObservableMap getDeviceSizes() {
return deviceSizes;
}
final ObservableMap deviceSizes = FXCollections.observableMap(new TreeMap(String.CASE_INSENSITIVE_ORDER));
/**
* Convenience method for deviceSizes.put(device.toString(), size)
*/
public void setDeviceSize(Device device, Size size) {
setDeviceSize(device.toString(), size);
}
/**
* Convenience method for deviceSizes.put(device, size)
*/
public void setDeviceSize(String device, Size size) {
deviceSizes.put(device, size);
}
/**
* Convienience method for getDeviceSize(device.toString())
*/
public Size getDeviceSize(Device device) {
return getDeviceSize(device.toString());
}
/**
* Convienience method for getDeviceSize(device)
*/
public Size getDeviceSize(String device) {
return deviceSizes.get(device);
}
// ==========================================================================================================================================================================================================================================
// LAYOUT
@Override
protected void layoutChildren() {
setupLayout();
// continue layout
super.layoutChildren();
}
/**
*
*/
void setupLayout() {
// recalculate ppi
ppi = null;
// layout
if (getTrace()) log("====================================================");
Layout lLayout = determineBestFittingLayout();
if (!lLayout.equals(getActiveLayout())) {
if (getDebug() || getTrace()) log("Activating layout " + lLayout);
setActiveLayout(lLayout);
// switch to active layout
ResponsivePane.this.getChildren().clear();
ResponsivePane.this.getChildren().add(lLayout.getRoot());
}
// scene stylesheet
if (getTrace()) log("----------------------------------------------------");
{
Stylesheet lStylesheet = determineBestFittingStylesheet(sceneStylesheets);
if (!lStylesheet.equals(getActiveSceneStylesheet())) {
setActiveSceneStylesheet(lStylesheet);
// switch to active CSS file
load(lStylesheet, sceneStylesheets, getScene().getStylesheets());
}
}
// my stylesheet
if (getTrace()) log("----------------------------------------------------");
{
Stylesheet lStylesheet = determineBestFittingStylesheet(myStylesheets);
if (!lStylesheet.equals(getActiveMyStylesheet())) {
setActiveMyStylesheet(lStylesheet);
// switch to active CSS file
load(lStylesheet, myStylesheets, getStylesheets());
}
}
if (getTrace()) log("====================================================");
}
// ==========================================================================================================================================================================================================================================
// SUPPORT
/**
* Determine the pixels-per-inch of the screen we are on
* @return
*/
double determinePPI() {
if (ppi != null) {
return ppi;
}
// Use command line value
if (System.getProperties().containsKey(PPI_SYSTEM_PROPERTY)) {
ppi = Double.valueOf(System.getProperty(PPI_SYSTEM_PROPERTY));
if (getTrace()) log("Using command line " + PPI_SYSTEM_PROPERTY + "=" + ppi);
return ppi;
}
// determine the DPI factor, so the thresholds become larger on screens with a higher DPI
ppi = 100.0; // average dpi
Window window = getScene().getWindow();
ObservableList screensForRectangle = Screen.getScreensForRectangle(window.getX(), window.getY(), window.getWidth(), window.getHeight());
if (screensForRectangle.size() > 0) {
Screen lScreen = screensForRectangle.get(0); // we just use the first screen
ppi = lScreen.getDpi();
if (getTrace()) log("screens of scene: " + screensForRectangle + ", using the first then PPI=" + ppi);
}
return ppi;
}
Double ppi;
final static public String PPI_SYSTEM_PROPERTY = ResponsivePane.class.getSimpleName() + ".ppi";
/**
*
* @return
*/
double determineActualDiagonalInInches() {
Scene lScene = getScene();
// determine the DPI factor, so the thresholds become larger on screens with a higher DPI
double lPPI = determinePPI();
double lWidthInInches = lScene.getWidth() / lPPI;
double lHeightInInches = lScene.getHeight() / lPPI;
double lDiagonalInInches = Math.sqrt( (lWidthInInches * lWidthInInches) + (lHeightInInches * lHeightInInches) );
if (getTrace()) log("Actual scene size=" + lScene.getWidth() + "x" + lScene.getHeight() + "px, scene diagonal in inches=" + lDiagonalInInches + " (ppi=" + lPPI + ")");
return lDiagonalInInches;
}
/**
*
* @return
*/
Layout determineBestFittingLayout() {
double actualDiagonalInInches = determineActualDiagonalInInches();
Scene lScene = getScene();
Orientation lSceneOrientation = (lScene.getWidth() > lScene.getHeight() ? Orientation.LANDSCAPE : Orientation.PORTRAIT);
// find the best fitting layout
Layout lBestFittingLayout = null;
for (Layout lLayout : layouts) {
if (getTrace()) log("determineBestFittingLayout, examining layout: " + lLayout.describeSizeConstraints());
double lLayoutSizeAtLeast = lLayout.getSizeAtLeast().toInches(this);
if (actualDiagonalInInches >= lLayoutSizeAtLeast) {
if (getTrace()) log("determineBestFittingLayout, layout " + lLayout.describeSizeConstraints() + " is candidate based on scene diagonal: " + actualDiagonalInInches + "in");
double lBestFittingLayoutSizeAtLeast = (lBestFittingLayout == null ? -1.0 : lBestFittingLayout.getSizeAtLeast().toInches(this));
if (getTrace()) log("determineBestFittingLayout, comparing best fitting layout " + (lBestFittingLayout == null ? "null" : lBestFittingLayout.describeSizeConstraints()) + " (" + lBestFittingLayoutSizeAtLeast + ") with " + lLayout.describeSizeConstraints() + " (" + lLayoutSizeAtLeast + ")");
if (lBestFittingLayout == null || lBestFittingLayoutSizeAtLeast <= lLayoutSizeAtLeast) {
if (getTrace()) log("determineBestFittingLayout, layout " + lLayout.describeSizeConstraints() + " is a better or equal candidate based on diagonal vs best fitting so far: " + (lBestFittingLayout == null ? "null" : lBestFittingLayout.describeSizeConstraints()));
// Based on the width, this layout is a candidate. But is it also based on the orientation?
Orientation lBestFittingLayoutOrientation = (lBestFittingLayout == null ? null : lBestFittingLayout.getOrientation());
Orientation lLayoutOrientation = lLayout.getOrientation();
if ( lBestFittingLayout == null || lLayoutOrientation == null || lSceneOrientation.equals(lLayoutOrientation)) {
if (getTrace()) log("determineBestFittingLayout, layout " + lLayout.describeSizeConstraints() + " is also candidate based on orientation");
// Orientation also matches, do we prefer the one orientation of the other?
if ( (lBestFittingLayoutOrientation == null && lLayoutOrientation == null)
|| (lBestFittingLayoutOrientation == null && lLayoutOrientation != null) // Prefer a layout with orientation above one without
) {
if (getTrace()) log("determineBestFittingLayout, layout " + lLayout.describeSizeConstraints() + " is also a better candidate based on orientation vs best fitting so far: " + (lBestFittingLayout == null ? "null" : lBestFittingLayout.describeSizeConstraints()));
lBestFittingLayout = lLayout;
if (getTrace()) log("determineBestFittingLayout, best fitting so far: " + lBestFittingLayout.describeSizeConstraints());
}
}
}
}
}
// if none found, use the default layout
if (lBestFittingLayout == null) {
lBestFittingLayout = SINGULARITY_LAYOUT;
}
// done
if (getTrace()) log("determineBestFittingLayout=" + lBestFittingLayout.describeSizeConstraints());
return lBestFittingLayout;
}
private final Layout SINGULARITY_LAYOUT = new Layout(Size.ZERO, new Label("?") );
/**
*
* @return
*/
Stylesheet determineBestFittingStylesheet(List availableStylesheets) {
double actualSizeInInches = determineActualDiagonalInInches();
// find the best fitting stylesheet
Stylesheet lBestFittingStylesheet = null;
for (Stylesheet lStylesheet : availableStylesheets) {
if (actualSizeInInches >= lStylesheet.getSizeAtLeast().toInches(this)) {
if (lBestFittingStylesheet == null || lBestFittingStylesheet.getSizeAtLeast().toInches(ResponsivePane.this) < lStylesheet.getSizeAtLeast().toInches(ResponsivePane.this)) {
lBestFittingStylesheet = lStylesheet;
}
}
}
// if none found, use the default Stylesheet
if (lBestFittingStylesheet == null) {
lBestFittingStylesheet = SINGULAR_STYLESHEET;
}
// done
if (getTrace()) log("determineBestFittingStylesheet=" + lBestFittingStylesheet.describeSizeConstraints() + " -> " + lBestFittingStylesheet.getFile());
return lBestFittingStylesheet;
}
final private Stylesheet SINGULAR_STYLESHEET = new Stylesheet(Size.ZERO, null);
/**
*
* @param stylesheet
*/
void load(Stylesheet stylesheet, List availableStylesheets, List activeStylesheetUrls) {
String lStylesheetFile = stylesheet.getFile();
// remove all of the available stylesheets
for (Stylesheet lStylesheet : availableStylesheets) {
String lActiveStylesheetUrl = lStylesheet.getFile();
if (activeStylesheetUrls.remove(lActiveStylesheetUrl)) {
if (getDebug() || getTrace()) log("Removed stylesheet " + lStylesheet.getSizeAtLeast() + " -> " + lActiveStylesheetUrl);
}
}
// load new
if (lStylesheetFile != null) {
if (getDebug() || getTrace()) log("Loading stylesheet " + stylesheet.getSizeAtLeast() + " -> " + lStylesheetFile);
activeStylesheetUrls.add(lStylesheetFile);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy