This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git commit 24c4bea42bc6333fa5c2ec86f270ac9becb6eb4b Author: Martin Desruisseaux AuthorDate: Mon Apr 13 22:30:47 2020 +0200 More control on the initial coverage view (image versus tabular data). --- .../java/org/apache/sis/gui/coverage/Controls.java | 7 + .../apache/sis/gui/coverage/CoverageExplorer.java | 148 ++++++++++++++++----- .../java/org/apache/sis/gui/coverage/GridView.java | 27 ++-- .../org/apache/sis/gui/coverage/GridViewSkin.java | 23 +--- .../org/apache/sis/gui/dataset/DataWindow.java | 12 +- .../apache/sis/gui/dataset/ResourceExplorer.java | 2 +- .../org/apache/sis/gui/dataset/SelectedData.java | 36 ++++- .../org/apache/sis/gui/dataset/WindowManager.java | 2 +- .../sis/internal/gui/NonNullObjectProperty.java | 55 ++++++++ 9 files changed, 249 insertions(+), 63 deletions(-) diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java index 1db40f7..7636376 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/Controls.java @@ -18,6 +18,7 @@ package org.apache.sis.gui.coverage; import javafx.geometry.Insets; import javafx.scene.Node; +import javafx.scene.control.ButtonBase; import javafx.scene.control.Control; import javafx.scene.control.Label; import javafx.scene.layout.Border; @@ -59,6 +60,12 @@ abstract class Controls { Styles.GROUP_BORDER, BorderStrokeStyle.SOLID, null, null)); /** + * The toolbar button for selecting this view. + * This is initialized after construction. + */ + ButtonBase selector; + + /** * Creates a new control. */ Controls() { diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java index f18e9c1..b57e342 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageExplorer.java @@ -16,13 +16,13 @@ */ package org.apache.sis.gui.coverage; +import javafx.scene.control.Control; import javafx.scene.control.SplitPane; import javafx.scene.control.Separator; import javafx.scene.control.ToggleGroup; import javafx.scene.control.Toggle; import javafx.scene.layout.Region; import javafx.event.ActionEvent; -import javafx.collections.ObservableList; import javafx.beans.value.ObservableValue; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -30,6 +30,7 @@ import org.apache.sis.coverage.grid.GridCoverage; import org.apache.sis.internal.gui.Resources; import org.apache.sis.internal.gui.Styles; import org.apache.sis.internal.gui.ToolbarButton; +import org.apache.sis.internal.gui.NonNullObjectProperty; import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.gui.Widget; @@ -49,13 +50,42 @@ import org.apache.sis.gui.Widget; */ public class CoverageExplorer extends Widget { /** - * Index in the {@link #views} array for the {@link GridView} (tabular data) - * or the {@link CoverageCanvas} (image). + * Type of view shown in the explorer. + * It may be either an image or a table of numerical values. */ - private static final int TABLE_VIEW = 0, IMAGE_VIEW = 1; + public enum View { + /** + * Shows the coverage numerical value in a table. This view uses {@link GridView}. + * This is the default value of newly constructed {@link CoverageExplorer}. + */ + TABLE("\uD83D\uDD22\uFE0F", Resources.Keys.Visualize), // 🔢 — Input symbol for numbers. + + /** + * Shows the coverage visual as an image. This view uses {@link CoverageCanvas}. + */ + IMAGE("\uD83D\uDDFA\uFE0F", Resources.Keys.TabularData); // 🗺 — World map. + + /** + * The Unicode characters to use as icon. + */ + final String icon; + + /** + * Key from {@link Resources} bundle for the localized text to use as tooltip. + */ + final short tooltip; + + /** + * Creates a new enumeration value. + */ + private View(final String icon, short tooltip) { + this.icon = icon; + this.tooltip = tooltip; + } + } /** - * The coverage shown in this view. Note that setting this property to a non-null value may not + * The coverage shown in this explorer. Note that setting this property to a non-null value may not * modify the view content immediately. Instead, a background process will request the tiles. * *

Current implementation is restricted to {@link GridCoverage} instances, but a future @@ -67,6 +97,20 @@ public class CoverageExplorer extends Widget { public final ObjectProperty coverageProperty; /** + * The type of view (image or tabular data) shown in this explorer. + * The default value is {@link View#TABLE}. + * + *

API note: + * the reason for setting default value to tabular data is because it requires loading much less data with + * {@link java.awt.image.RenderedImage}s supporting deferred tile loading. By contrast {@link View#IMAGE} + * may require loading the full image.
+ * + * @see #getViewType() + * @see #setViewType(View) + */ + public final ObjectProperty viewTypeProperty; + + /** * Whether the {@link #coverageProperty} is in process of being set, in which case some * listeners should not react. */ @@ -91,56 +135,62 @@ public class CoverageExplorer extends Widget { */ public CoverageExplorer() { coverageProperty = new SimpleObjectProperty<>(this, "coverage"); + viewTypeProperty = new NonNullObjectProperty<>(this, "viewType", View.TABLE); coverageProperty.addListener(this::onCoverageSpecified); + viewTypeProperty.addListener(this::onViewTypeSpecified); + /* + * Prepare buttons to add on the toolbar. Those buttons are not managed by this class; + * they are managed by org.apache.sis.gui.dataset.DataWindow. We only declare here the + * text and action for each button. + */ + final View[] viewTypes = View.values(); + final ToggleGroup group = new ToggleGroup(); + final Control[] buttons = new Control[viewTypes.length + 1]; + buttons[0] = new Separator(); /* * The coverage property may be shown in various ways (tabular data, image). * Each visualization way is an entry in the `views` array. */ final Resources localized = Resources.forLocale(null); final Vocabulary vocabulary = Vocabulary.getResources(localized.getLocale()); - views = new Controls[2]; - views[TABLE_VIEW] = new GridControls(vocabulary); - views[IMAGE_VIEW] = new CoverageControls(vocabulary, coverageProperty); - for (final Controls c : views) { + views = new Controls[viewTypes.length]; + for (final View type : viewTypes) { + final Controls c; + switch (type) { + case TABLE: c = new GridControls(vocabulary); break; + case IMAGE: c = new CoverageControls(vocabulary, coverageProperty); break; + default: throw new AssertionError(type); + } SplitPane.setResizableWithParent(c.controls(), Boolean.FALSE); SplitPane.setResizableWithParent(c.view(), Boolean.TRUE); + c.selector = new Selector(type).createButton(group, type.icon, localized, type.tooltip); + buttons[buttons.length - type.ordinal() - 1] = c.selector; // Buttons in reverse order. + views[type.ordinal()] = c; } - final Controls c = views[TABLE_VIEW]; + final Controls c = views[0]; // First View enumeration is default value. + group.selectToggle(group.getToggles().get(0)); content = new SplitPane(c.controls(), c.view()); content.setDividerPosition(0, Styles.INITIAL_SPLIT); - /* - * Prepare buttons to add on the toolbar. Those buttons are not managed by this class; - * they are managed by org.apache.sis.gui.dataset.DataWindow. We only declare here the - * text and action for each button. - */ - final ToggleGroup group = new ToggleGroup(); - ToolbarButton.insert(content, - new Separator(), - new Selector(IMAGE_VIEW).createButton(group, "\uD83D\uDDFA\uFE0F", localized, Resources.Keys.Visualize), // 🗺 — World map. - new Selector(TABLE_VIEW).createButton(group, "\uD83D\uDD22\uFE0F", localized, Resources.Keys.TabularData) // 🔢 — Input symbol for numbers. - ); - final ObservableList toggles = group.getToggles(); - group.selectToggle(toggles.get(toggles.size() - 1)); + ToolbarButton.insert(content, buttons); } /** * The action to execute when the user selects a view. */ private final class Selector extends ToolbarButton { - /** {@link #TABLE_VIEW} or {@link #IMAGE_VIEW}. */ - private final int index; + /** The view to select when the button is pressed. */ + private final View view; /** Creates a new action which will show the view at the given index. */ - Selector(final int index) { - this.index = index; + Selector(final View view) { + this.view = view; } /** Invoked when the user selects another view to show (tabular data or the image). */ @Override public void handle(final ActionEvent event) { final Toggle button = (Toggle) event.getSource(); if (button.isSelected()) { - final Controls c = views[index]; - content.getItems().setAll(c.controls(), c.view()); + setViewType(view); } else { button.setSelected(true); // Prevent situation where all buttons are unselected. } @@ -246,7 +296,7 @@ public class CoverageExplorer extends Widget { * @param source the coverage or resource to load, or {@code null} if none. */ private void startLoading(final ImageRequest source) { - final GridView main = (GridView) views[TABLE_VIEW].view(); + final GridView main = (GridView) views[View.TABLE.ordinal()].view(); main.setImage(source); } @@ -261,4 +311,42 @@ public class CoverageExplorer extends Widget { c.updateBandTable(data); } } + + /** + * Returns the type of view (image or tabular data) shown in this explorer. + * The default value is {@link View#TABLE}. + * + * @return the type of view shown in this explorer. + * + * @see #viewTypeProperty + */ + public final View getViewType() { + return viewTypeProperty.get(); + } + + /** + * Sets the type of view to show in this explorer. + * + * @param coverage the type of view to show in this explorer. + * + * @see #viewTypeProperty + */ + public final void setViewType(final View coverage) { + viewTypeProperty.set(coverage); + } + + /** + * Invoked when a new view type has been specified. + * + * @param property the {@link #viewTypeProperty} (ignored). + * @param previous ignored. + * @param view the new view type. + */ + private void onViewTypeSpecified(final ObservableValue property, + final View previous, final View view) + { + final Controls c = views[view.ordinal()]; + content.getItems().setAll(c.controls(), c.view()); + ((Toggle) c.selector).setSelected(true); + } } diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java index 125d510..b518a54 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java @@ -41,6 +41,7 @@ import org.apache.sis.util.ArgumentChecks; import org.apache.sis.coverage.grid.GridCoverage; import org.apache.sis.internal.gui.BackgroundThreads; import org.apache.sis.internal.gui.Styles; +import org.apache.sis.gui.map.StatusBar; /** @@ -198,6 +199,11 @@ public class GridView extends Control { final CellFormat cellFormat; /** + * The status bar where to show coordinates of selected cell. + */ + final StatusBar statusBar; + + /** * Creates an initially empty grid view. The content can be set after * construction by a call to {@link #setImage(RenderedImage)}. */ @@ -211,6 +217,7 @@ public class GridView extends Control { headerBackground = new SimpleObjectProperty<>(this, "headerBackground", Color.GAINSBORO); headerFormat = NumberFormat.getIntegerInstance(); cellFormat = new CellFormat(this); + statusBar = new StatusBar(); tileWidth = 1; tileHeight = 1; // For avoiding division by zero. @@ -304,7 +311,7 @@ public class GridView extends Control { loader = null; final ImageLoader result = (ImageLoader) event.getSource(); setImage(result.getValue()); - result.request.configure(((GridViewSkin) getSkin()).statusBar); + result.request.configure(statusBar); } /** @@ -415,14 +422,6 @@ public class GridView extends Control { } /** - * Returns the offset to add for converting cell indices to pixel indices. They are often the same indices, - * but may differ if the {@link RenderedImage} uses a coordinate system where coordinates of the upper-left - * corner is not (0,0). - */ - final int getImageMinX() {return minX;} - final int getImageMinY() {return minY;} - - /** * Returns the bounds of a single tile in the image. This method is invoked only * if an error occurred during {@link RenderedImage#getTile(int, int)} invocation. * The returned bounds are zero-based (may not be the bounds in image coordinates). @@ -527,6 +526,16 @@ public class GridView extends Control { } /** + * Converts and formats the given cell coordinates. An offset is added to the coordinate values for converting + * cell indices to pixel indices. Those two kind of indices often have the same values, but may differ if the + * {@link RenderedImage} uses a coordinate system where coordinates of the upper-left corner is not (0,0). + * Then the pixel coordinates are converted to "real world" coordinates and formatted. + */ + final void formatCoordinates(final int x, final int y) { + statusBar.setLocalCoordinates(minX + x, minY + y); + } + + /** * Creates a new instance of the skin responsible for rendering this grid view. * From the perspective of this {@link Control}, the {@link Skin} is a black box. * It listens and responds to changes in state of this grid view. This method is diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java index 7b80ede..bd306ca 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java @@ -33,7 +33,6 @@ import javafx.scene.text.FontWeight; import javafx.scene.text.Font; import javafx.scene.input.MouseEvent; import javafx.event.EventHandler; -import org.apache.sis.gui.map.StatusBar; import org.apache.sis.internal.gui.Styles; @@ -133,11 +132,6 @@ final class GridViewSkin extends VirtualContainerBase impleme private final Rectangle selection, selectedRow, selectedColumn; /** - * The status bar where to show coordinates of selected cell. - */ - final StatusBar statusBar; - - /** * Creates a new skin for the specified view. */ GridViewSkin(final GridView view) { @@ -179,14 +173,13 @@ final class GridViewSkin extends VirtualContainerBase impleme * The status bar where to show coordinates of selected cell. * Mouse exit event is handled by `hideSelection(…)`. */ - statusBar = new StatusBar(); - flow.setOnMouseEntered(statusBar); + flow.setOnMouseEntered(view.statusBar); /* * The list of children is initially empty. We need to * add the virtual flow, otherwise nothing will appear. */ getChildren().addAll(topBackground, leftBackground, selectedColumn, selectedRow, - headerRow, selection, statusBar.getView(), flow); + headerRow, selection, view.statusBar.getView(), flow); } /** @@ -213,9 +206,7 @@ final class GridViewSkin extends VirtualContainerBase impleme selection.relocate(x, y); selectedRow.setY(y); selectedColumn.setX(x); - final GridView view = getSkinnable(); - statusBar.setLocalCoordinates(view.getImageMinX() + ((int) visibleColumn) + firstVisibleColumn, - view.getImageMinY() + row.getIndex()); + getSkinnable().formatCoordinates(((int) visibleColumn) + firstVisibleColumn, row.getIndex()); } } } @@ -223,7 +214,7 @@ final class GridViewSkin extends VirtualContainerBase impleme selectedRow .setVisible(visible); selectedColumn.setVisible(visible); if (!visible) { - statusBar.handle(null); + getSkinnable().statusBar.handle(null); } } @@ -234,7 +225,7 @@ final class GridViewSkin extends VirtualContainerBase impleme selection .setVisible(false); selectedRow .setVisible(false); selectedColumn.setVisible(false); - statusBar .handle (null); // Hide the coordinates. + getSkinnable().statusBar.handle(null); // Hide the coordinates. } /** @@ -442,7 +433,7 @@ final class GridViewSkin extends VirtualContainerBase impleme final Flow flow = (Flow) getVirtualFlow(); final double cellHeight = flow.getFixedCellSize(); final double headerHeight = cellHeight + 2*cellSpacing; - final double statusHeight = statusBar.getView().getHeight(); + final double statusHeight = view.statusBar.getView().getHeight(); final double dataY = y + headerHeight; final double dataHeight = height - headerHeight - statusHeight; layoutAll |= (flow.getWidth() != width) || (flow.getHeight() != dataHeight); @@ -491,7 +482,7 @@ final class GridViewSkin extends VirtualContainerBase impleme if (layoutAll || oldPos != leftPosition) { layoutInArea(headerRow, x, y, width, headerHeight, Node.BASELINE_OFFSET_SAME_AS_HEIGHT, HPos.LEFT, VPos.TOP); - layoutInArea(statusBar.getView(), x, height - statusHeight, width, statusHeight, + layoutInArea(view.statusBar.getView(), x, height - statusHeight, width, statusHeight, Node.BASELINE_OFFSET_SAME_AS_HEIGHT, HPos.RIGHT, VPos.BOTTOM); final ObservableList children = headerRow.getChildren(); final int count = children.size(); diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/DataWindow.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/DataWindow.java index a4f45fc..d1ca566 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/DataWindow.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/DataWindow.java @@ -16,6 +16,7 @@ */ package org.apache.sis.gui.dataset; +import java.util.EventObject; import javafx.geometry.Rectangle2D; import javafx.stage.Screen; import javafx.stage.Stage; @@ -55,11 +56,12 @@ final class DataWindow extends Stage { * Creates a new window for the given data selected in the explorer or determined by the active tab. * The new window will be positioned in the screen center but not yet shown. * - * @param home the window containing the main explorer, to be the target of "home" button. - * @param data the data selected by user, to show in a new window. + * @param event the event (e.g. mouse action) requesting a new window, or {@code null} if unknown. + * @param home the window containing the main explorer, to be the target of "home" button. + * @param data the data selected by user, to show in a new window. */ - DataWindow(final Stage home, final SelectedData data) { - final Region content = data.createView(); + DataWindow(final EventObject event, final Stage home, final SelectedData data) { + final Region content = data.createView(event); /* * Build the tools bar. This bar will be hidden in full screen mode. Note that above * method assumes that the "home" button created below is the first one in the toolbar. @@ -70,7 +72,7 @@ final class DataWindow extends Stage { final Button fullScreen = new Button("\u21F1\uFE0F"); // ⇱ — North West Arrow to Corner fullScreen.setTooltip(new Tooltip(data.localized.getString(Resources.Keys.FullScreen))); - fullScreen.setOnAction((event) -> setFullScreen(true)); + fullScreen.setOnAction((e) -> setFullScreen(true)); fullScreenProperty().addListener((source, oldValue, newValue) -> onFullScreen(newValue)); tools = new ToolBar(mainWindow, fullScreen); diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java index d7672b8..27a6dcf 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java @@ -113,7 +113,7 @@ public class ResourceExplorer extends WindowManager { final Resources localized = localized(); dataTab = new Tab(localized.getString(Resources.Keys.Data)); - dataTab.setContextMenu(new ContextMenu(createNewWindowMenu())); + dataTab.setContextMenu(new ContextMenu(SelectedData.setTabularView(createNewWindowMenu()))); final String nativeTabText = Vocabulary.getResources(localized.getLocale()).getString(Vocabulary.Keys.Format); final MetadataTree nativeMetadata = new MetadataTree(metadata); diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/SelectedData.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/SelectedData.java index a7af6e2..efa04b2 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/SelectedData.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/SelectedData.java @@ -16,7 +16,9 @@ */ package org.apache.sis.gui.dataset; +import java.util.EventObject; import javafx.scene.layout.Region; +import javafx.scene.control.MenuItem; import org.apache.sis.gui.coverage.CoverageExplorer; import org.apache.sis.gui.coverage.ImageRequest; import org.apache.sis.internal.gui.Resources; @@ -42,6 +44,12 @@ import org.apache.sis.internal.gui.Resources; */ final class SelectedData { /** + * Key of a property for storing {@link CoverageExplorer.View} value + * specifying the initial view of new windows. + */ + private static final String COVERAGE_VIEW_KEY = "org.apache.sis.gui.CoverageView"; + + /** * A title to use for windows and menu items. */ final String title; @@ -74,14 +82,40 @@ final class SelectedData { } /** + * Specifies that the given menu item should create a window initialized to tabular data + * instead than the image. + */ + static MenuItem setTabularView(final MenuItem item) { + item.getProperties().put(COVERAGE_VIEW_KEY, CoverageExplorer.View.TABLE); + return item; + } + + /** * Creates the view for selected data. + * + * @param event the event (e.g. mouse action) requesting a new window, or {@code null} if unknown. */ - final Region createView() { + final Region createView(final EventObject event) { if (features != null) { return new FeatureTable(features); } else { + CoverageExplorer.View view = CoverageExplorer.View.IMAGE; + if (event != null) { + final Object source = event.getSource(); + if (source instanceof MenuItem) { + final Object value = ((MenuItem) source).getProperties().get(COVERAGE_VIEW_KEY); + if (value instanceof CoverageExplorer.View) { + view = (CoverageExplorer.View) value; + } + } + } final CoverageExplorer ce = new CoverageExplorer(); ce.setCoverage(coverage); + /* + * TODO: following line is disabled for now because it causes + * the vertical scroll bar of `GridView` to disappear. + */ +// ce.setViewType(view); return ce.getView(); } } diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowManager.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowManager.java index bb9f6c5..fcbf302 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowManager.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowManager.java @@ -154,7 +154,7 @@ abstract class WindowManager extends Widget { private void newDataWindow(final ActionEvent event) { final SelectedData selection = getSelectedData(); if (selection != null) { - final DataWindow window = new DataWindow((Stage) getView().getScene().getWindow(), selection); + final DataWindow window = new DataWindow(event, (Stage) getView().getScene().getWindow(), selection); window.setTitle(selection.title + " — Apache SIS"); window.show(); if (showWindowMenus != null) { diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/NonNullObjectProperty.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/NonNullObjectProperty.java new file mode 100644 index 0000000..85175ee --- /dev/null +++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/NonNullObjectProperty.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.gui; + +import java.util.Objects; +import javafx.beans.property.SimpleObjectProperty; + + +/** + * A simple property implementation which does not accept null values. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.1 + * + * @param the type of the wrapped object. + * + * @since 1.1 + * @module + */ +public final class NonNullObjectProperty extends SimpleObjectProperty { + /** + * Creates a new property. + * + * @param bean object for which this property is a member. + * @param name name of the property in the bean. + * @param initialValue initial value of the property. + */ + public NonNullObjectProperty(Object bean, String name, T initialValue) { + super(bean, name, initialValue); + } + + /** + * Sets the property value. + * + * @param newValue the new property value. + */ + @Override + public void set​(final T newValue) { + super.set(Objects.requireNonNull(newValue)); + } +}