From 91bb7c256a1d91e192a23c56b63ae689cea8b4b2 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Thu, 20 Jun 2019 11:33:40 +0200 Subject: [PATCH 01/17] Add a BrowserInterface to the SpreadsheetView for enabling RichText support --- .../samples/spreadsheet/BrowserImpl.java | 90 ++++++++++++ .../org/controlsfx/spreadsheet/CellView.java | 128 ++++++++++++------ .../controlsfx/spreadsheet/CellViewSkin.java | 16 ++- .../control/spreadsheet/BrowserInterface.java | 90 ++++++++++++ .../control/spreadsheet/ClipboardCell.java | 40 +++++- .../control/spreadsheet/SpreadsheetCell.java | 20 ++- .../spreadsheet/SpreadsheetCellBase.java | 11 +- .../control/spreadsheet/SpreadsheetView.java | 47 +++++-- 8 files changed, 377 insertions(+), 65 deletions(-) create mode 100644 controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java create mode 100644 controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java diff --git a/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java new file mode 100644 index 000000000..076ea6253 --- /dev/null +++ b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2019 ControlsFX + * 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 ControlsFX, any associated website, 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 CONTROLSFX 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 org.controlsfx.samples.spreadsheet; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.text.Font; +import javafx.scene.web.WebView; +import org.controlsfx.control.spreadsheet.BrowserInterface; + +/** + * + * Example of implementation of the BrowserInterface in order to provide WebView + * inside of cells. + */ +public class BrowserImpl implements BrowserInterface { + + private final LinkedList browserList = new LinkedList<>(); + private final AtomicInteger browserCounter = new AtomicInteger(); + private final Map loadedUrl = new HashMap<>(); + + @Override + public Node getBrowser(Object url) { + //Limit the number of browser displayed. + if (browserCounter.get() < 256) { + if (browserList.isEmpty()) { + browserCounter.incrementAndGet(); + WebView webView = new WebView(); + webView.getEngine().loadContent(url.toString()); + loadedUrl.put(webView, url.toString()); + return webView; + } else { + WebView webView = browserList.pop(); + webView.getEngine().loadContent(url.toString()); + return webView; + } + } else { + return new Label("Too many browser"); + } + } + + @Override + public void load(WebView browser, Object url) { + if (!loadedUrl.containsKey(browser) || !loadedUrl.get(browser).equals(url)) { + browser.getEngine().loadContent(url.toString()); + loadedUrl.put(browser, url.toString()); + } + } + + public void loadStyle(WebView browser, Font font) { + //no-op + } + + @Override + public void setUnusedBrowser(WebView browser) { + browserList.add(browser); + } + + public Class getType() { + return WebView.class; + } +} diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java index 7b7888c08..10ec7e495 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013, 2018 ControlsFX + * Copyright (c) 2013, 2019 ControlsFX * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,7 @@ import java.util.Optional; import java.util.logging.Logger; import javafx.animation.FadeTransition; -import javafx.application.Platform; import javafx.beans.binding.When; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import javafx.beans.value.WeakChangeListener; import javafx.collections.ObservableList; import javafx.collections.SetChangeListener; @@ -42,7 +39,6 @@ import javafx.event.WeakEventHandler; import javafx.geometry.Side; import javafx.scene.Cursor; -import javafx.scene.Node; import javafx.scene.control.ContentDisplay; import javafx.scene.control.ContextMenu; import javafx.scene.control.Control; @@ -59,16 +55,21 @@ import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; -import javafx.scene.layout.Region; import javafx.util.Duration; import org.controlsfx.control.spreadsheet.Filter; import org.controlsfx.control.spreadsheet.SpreadsheetCell; import org.controlsfx.control.spreadsheet.SpreadsheetCellEditor; import org.controlsfx.control.spreadsheet.SpreadsheetCellType; import org.controlsfx.control.spreadsheet.SpreadsheetView; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.scene.Node; +import javafx.scene.layout.Region; +import org.controlsfx.control.spreadsheet.BrowserInterface; /** - * + * * The View cell that will be visible on screen. It holds the * {@link SpreadsheetCell}. */ @@ -83,6 +84,8 @@ public class CellView extends TableCell, Spreads //Handler for drag n drop in lazy instantiation. private EventHandler dragOverHandler; private EventHandler dragDropHandler; + //Set to true when the style has changed + private boolean dirtyStyle = false; /*************************************************************************** * * Static Fields * * @@ -106,7 +109,7 @@ static void setAnchor(Control table, TablePositionBase anchor) { table.getProperties().put(ANCHOR_PROPERTY_KEY, anchor); } } - + static void removeAnchor(Control table) { table.getProperties().remove(ANCHOR_PROPERTY_KEY); } @@ -144,12 +147,12 @@ public void startEdit() { updateTableColumn(null); return; } - + if (!isEditable()) { getTableView().edit(-1, null); return; - } - + } + final int column = this.getTableView().getColumns().indexOf(this.getTableColumn()); final int row = getIndex(); // We start to edit only if the Cell is a normal Cell (aka visible). @@ -162,7 +165,7 @@ public void startEdit() { * We may come to the situation where this method is called two * times. One time by the row inside the VirtualFlow. And another by * the row inside myFixedCells used by our GridVirtualFlow. - * + * * In that case, we have to give priority to the one used by the * VirtualFlow. So we just check if the row is managed. If not, we * know for sure that the our GridVirtualFlow has stepped out. @@ -184,7 +187,7 @@ public void startEdit() { /** * Return the Filter associated to this cell or null otherwise. - * @return + * @return */ Filter getFilter() { Filter filter = getItem() != null && getItem().getColumn() < handle.getView().getColumns().size() ? handle.getView().getColumns().get(getItem().getColumn()).getFilter() : null; @@ -211,7 +214,7 @@ public void commitEdit(SpreadsheetCell newValue) { fadeTransition.setFromValue(0); fadeTransition.setToValue(1); fadeTransition.play(); - + if (!isEditing()) { return; } @@ -253,7 +256,7 @@ public void updateItem(final SpreadsheetCell item, boolean empty) { * don't call super.updateItem() because it will trigger cancelEdit() if * the cell is being edited. It causes calling commitEdit() ALWAYS call * cancelEdit as well which is undesired. - * + * */ if (!isEditing()) { super.updateItem(item, empty && emptyRow); @@ -264,14 +267,31 @@ public void updateItem(final SpreadsheetCell item, boolean empty) { if (empty && emptyRow) { textProperty().unbind(); setText(null); + //Release any browser we might have + BrowserInterface browserImpl = handle.getView().getBrowser(); + if (browserImpl != null && getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { + browserImpl.setUnusedBrowser(getGraphic()); + setGraphic(null); + } } else if (!isEditing() && item != null) { show(item); - if (item.getGraphic() == null) { + if (item.getGraphic() == null && !item.isBrowser()) { setGraphic(null); } } } + @Override + protected void layoutChildren() { + super.layoutChildren(); + BrowserInterface browserImpl = handle.getView().getBrowser(); + //When layout is called, the Font has been set on the cell, we can give it to the browserInterface + if (dirtyStyle && browserImpl != null && getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { + browserImpl.loadStyle(getGraphic(), getFont()); + dirtyStyle = false; + } + } + /** * Called in the gridRowSkinBase when doing layout This allow not to * override opacity in the row and let the cell handle itself @@ -284,7 +304,7 @@ public void show(final SpreadsheetCell cell) { Optional tooltipText = cell.getTooltip(); String trimTooltip = tooltipText.isPresent() ? tooltipText.get().trim() : null; - + if (trimTooltip != null && !trimTooltip.isEmpty()) { /** * Here we check if the Tooltip has not been created in order NOT TO @@ -316,11 +336,11 @@ public void show(final SpreadsheetCell cell) { } setTooltip(null); } - + setWrapText(cell.isWrapText()); setEditable(cell.hasPopup() ? false : cell.isEditable()); - + if (cell.hasPopup()) { setOnMouseClicked(weakActionhandler); setCursor(Cursor.HAND); @@ -358,7 +378,7 @@ private Tooltip getAvailableTooltip(){ } return null; } - + /** * Return true if this cell is the original cell (including filters) even * when scrolling. @@ -369,7 +389,25 @@ private Tooltip getAvailableTooltip(){ public boolean isOriginalCell() { return handle.getView().getReverseRowSpan(getItem(), getIndex()) <= 1; } - + + /** + * Sets the browser in the cell. + * @param item + */ + private void setBrowserGraphic(SpreadsheetCell item) { + BrowserInterface browserImpl = handle.getView().getBrowser(); + if (browserImpl == null) { + return; + } + textProperty().unbind(); + setText(null); + if (getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { + browserImpl.load(getGraphic(), item.getItem()); + } else { + setGraphic(browserImpl.getBrowser(item.getItem())); + } + } + /** * Set the cell graphic if any. * @@ -383,6 +421,13 @@ private void setCellGraphic(SpreadsheetCell item) { if (isEditing()) { return; } + if (item.isBrowser() && item.getItem() != null) { + setBrowserGraphic(item); + return; + } else if (getGraphic() != null && handle.getView().getBrowser() != null && handle.getView().getBrowser().getType().isAssignableFrom(getGraphic().getClass())) { + handle.getView().getBrowser().setUnusedBrowser(getGraphic()); + setGraphic(null); + } Node graphic = item.getGraphic(); if (graphic != null) { if (graphic instanceof ImageView) { @@ -391,12 +436,12 @@ private void setCellGraphic(SpreadsheetCell item) { image.setPreserveRatio(true); image.setSmooth(true); if(image.getImage() != null){ - image.fitHeightProperty().bind( - new When(heightProperty().greaterThan(image.getImage().getHeight())).then( - image.getImage().getHeight()).otherwise(heightProperty())); - image.fitWidthProperty().bind( - new When(widthProperty().greaterThan(image.getImage().getWidth())).then( - image.getImage().getWidth()).otherwise(widthProperty())); + image.fitHeightProperty().bind( + new When(heightProperty().greaterThan(image.getImage().getHeight())).then( + image.getImage().getHeight()).otherwise(heightProperty())); + image.fitWidthProperty().bind( + new When(widthProperty().greaterThan(image.getImage().getWidth())).then( + image.getImage().getWidth()).otherwise(widthProperty())); } /** * If we have a Region and no text, we force it to take full @@ -428,7 +473,7 @@ private void setCellGraphic(SpreadsheetCell item) { * the build-in editor-Cell because we cannot know in advance which editor * we will need. Furthermore, we want to control the behavior very closely * in regards of the spanned cell (invisible etc). - * + * * @param cell * The SpreadsheetCell * @param bc @@ -461,7 +506,7 @@ private GridCellEditor getEditor(final SpreadsheetCell cell, final SpreadsheetVi editor.endEdit(false); } - + editor.updateSpreadsheetCell(this); editor.updateDataCell(cell); editor.updateSpreadsheetCellEditor(cellEditor.get()); @@ -470,7 +515,7 @@ private GridCellEditor getEditor(final SpreadsheetCell cell, final SpreadsheetVi return null; } } - + private EventHandler getDragOverHandler() { if (dragOverHandler == null) { dragOverHandler = new EventHandler() { @@ -519,7 +564,7 @@ public void changed(ObservableValue arg0, Node arg1, Node newGra }; private final WeakChangeListener weakGraphicListener = new WeakChangeListener<>(graphicListener); - + private final SetChangeListener styleClassListener = new SetChangeListener() { @Override public void onChanged(javafx.collections.SetChangeListener.Change arg0) { @@ -528,19 +573,20 @@ public void onChanged(javafx.collections.SetChangeListener.Change weakStyleClassListener = new WeakSetChangeListener<>(styleClassListener); - + //Listeners for the styles, not initialized by default in order not to impact performance private ChangeListener styleListener; private WeakChangeListener weakStyleListener; - + /** * Method that will select all the cells between the drag place and that * cell. - * + * * @param e */ private void dragSelect(MouseEvent e) { @@ -614,7 +660,7 @@ private void dragSelect(MouseEvent e) { /** * Will safely execute the request on the JFX thread by checking whether we * are on the JFX thread or not. - * + * * @param runnable */ public static void getValue(final Runnable runnable) { @@ -639,14 +685,14 @@ public void handle(MouseEvent arg0) { } } }; - + private final EventHandler dragMouseEventHandler = new EventHandler() { @Override public void handle(MouseEvent arg0) { dragSelect(arg0); } }; - + private final ChangeListener itemChangeListener = new ChangeListener() { @Override @@ -676,10 +722,11 @@ public void changed(ObservableValue arg0, Spreadsheet //We clear the previous style. setStyle(null); } + dirtyStyle = true; } } }; - + /** * Event Handler when the cell is simply clicked in order to display the * possible actions in MenuItem. @@ -703,11 +750,12 @@ public void handle(MouseEvent event) { } }; private final WeakEventHandler weakActionhandler = new WeakEventHandler(actionEventHandler); - + private void initStyleListener(){ if(styleListener == null){ styleListener = (ObservableValue observable, String oldValue, String newValue) -> { styleProperty().set(newValue); + dirtyStyle = true; }; } weakStyleListener = new WeakChangeListener<>(styleListener); diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellViewSkin.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellViewSkin.java index df47a9c96..0a54c844f 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellViewSkin.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellViewSkin.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013, 2018 ControlsFX + * Copyright (c) 2013, 2019 ControlsFX * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,6 +45,7 @@ import javafx.scene.control.TableColumn; import javafx.scene.image.ImageView; import javafx.scene.layout.Region; +import javafx.scene.web.WebView; import org.controlsfx.control.spreadsheet.Filter; import org.controlsfx.control.spreadsheet.SpreadsheetCell; import org.controlsfx.control.spreadsheet.SpreadsheetCell.CornerPosition; @@ -96,6 +97,12 @@ public CellViewSkin(CellView tableCell) { @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { + Node graphic = getSkinnable().getGraphic(); + //WebView resize is not working so we do not consider it right now + if (graphic instanceof WebView) { + //WebView must not impact autofit + return -1; + } /** * If we have an Image in the Cell, its fitHeight will be affected by * the cell height (see CellView). But during calculation for autofit @@ -103,7 +110,6 @@ protected double computePrefHeight(double width, double topInset, double rightIn * the fitHeight option is returned by default so we must override and * return the Height of the image inside. */ - Node graphic = getSkinnable().getGraphic(); if (graphic != null && graphic instanceof ImageView) { ImageView view = (ImageView) graphic; if (!((CellView) getSkinnable()).isOriginalCell()) { @@ -120,6 +126,12 @@ protected double computePrefHeight(double width, double topInset, double rightIn @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { + Node graphic = getSkinnable().getGraphic(); + //WebView resize is not working so we do not consider it right now + if(graphic instanceof WebView){ + //WebView must not impact autofit + return -1; + } /** * We integrate the filter width into the total width for autosize. We * do just like Excel. diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java new file mode 100644 index 000000000..38413a767 --- /dev/null +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2019 ControlsFX + * 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 ControlsFX, any associated website, 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 CONTROLSFX 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 org.controlsfx.control.spreadsheet; + +import javafx.scene.Node; +import javafx.scene.text.Font; +import javafx.scene.web.WebView; + +/** + * + * If anyone wants to display Rich Text in a SpreadsheetCell, a solution is to + * provide a Browser (for example a {@link WebView}) that will be displayed in + * the cell. + * + * Because a browser consumes a lot of memory, we need this BrowserInterface + * that will recycle the Browser to only provide them for visible cells. + */ +public interface BrowserInterface { + + /** + * Returns a {@code Node} to display in the cell graphic. This is called + * internally by the SpreadsheetView when a cell is being visible and needs + * to display a browser. + * + * @param itemValue what is contained in {@link SpreadsheetCell#getItem() } + * @return a {@code Node} to display in the cell graphic + */ + public Node getBrowser(Object itemValue); + + /** + * When a browser is reused (transfered from one cell to another for + * example), we ask the browser to reload. Beware, only reload when + * necessary! This method can be called several times with the same browser + * and itemValue. + * + * @param browser the considered browser + * @param itemValue what is contained in {@link SpreadsheetCell#getItem() } + */ + public void load(T browser, Object itemValue); + + /** + * Once a {@code SpreadsheetCell} has been effectively loaded in the grid, + * this method is called if the browser wants to access the cell's font. + * + * @param browser the considered browsr + * @param font the cell font + */ + public void loadStyle(T browser, Font font); + + /** + * Once a browser is no longer used in a cell, it is given back. + * + * @param browser the browser + */ + public void setUnusedBrowser(T browser); + + /** + * Returns the exact class used in this BrowserInterface. It is used to + * determine if the cell's graphic is handled by the cell of by this + * interface. + * + * @return the exact class used in this BrowserInterface + */ + public Class getType(); + +} diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/ClipboardCell.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/ClipboardCell.java index 394c46697..b4867e924 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/ClipboardCell.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/ClipboardCell.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 ControlsFX + * Copyright (c) 2018, 2019 ControlsFX * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,6 +26,9 @@ */ package org.controlsfx.control.spreadsheet; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; import java.io.Serializable; import javafx.scene.input.Clipboard; @@ -42,21 +45,35 @@ public class ClipboardCell implements Serializable { private final int row; private final int column; - private final Object value; + private Object value; + private final String htmlVersion; /** * Constructor of a ClipboardCell for a cell. * - * The value must be serializable. + * The SpreadsheetCell item must be serializable. * * @param row the row of this {@code ClipboardCell} * @param column the column of this {@code ClipboardCell} - * @param value the value of this {@code ClipboardCell} + * @param spc the SpreadsheetCell which value will be serialized */ - public ClipboardCell(int row, int column, Object value) { + public ClipboardCell(int row, int column, SpreadsheetCell spc) { this.row = row; this.column = column; - this.value = value; + Object value = spc.getItem(); + if (spc.isBrowser()) { + this.htmlVersion = value == null ? null : value.toString(); + //Trust the SpreadsheetCellType to return a proper String version of the HTML + this.value = spc.getCellType().toString(spc.getItem()); + } else { + try { + new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(value); + this.value = value; + } catch (IOException exception) { + this.value = value == null ? null : value.toString(); + } + this.htmlVersion = null; + } } /** @@ -85,4 +102,15 @@ public int getColumn() { public Object getValue() { return value; } + + /** + * If the original cell had its {@link SpreadsheetCell#isBrowser() } to + * {@code true}, the cell HTML content will be here and the string version + * in {@link #getValue() }. + * + * @return the html version of the value + */ + public String getHtmlVersion() { + return htmlVersion; + } } diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java index 549678136..ae9465436 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java @@ -138,7 +138,25 @@ public static enum CornerPosition { * exceeds the width of the {@code Labeled} */ public boolean isWrapText(); - + + /** + * Returns {@code true} if this cell contains HTML (URL or pure HTML) in its + * item and a browser given by the {@link BrowserInterface} will be used to + * display it. + * + * @return {@code true} if this cell contains HTML + */ + public boolean isBrowser(); + + /** + * If {@code isBrowser} is {@code true}, this cell item contains some HTML + * and should be display by using {@link BrowserInterface} object in the + * CellView. + * + * @param isBrowser if {@code true}, a browser will be used to display HTML + * for the cell + */ + public void setBrowser(boolean isBrowser); /** * If a run of text exceeds the width of the Labeled, then this variable diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java index 7b8a7fdb4..de8e3c5fa 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java @@ -220,6 +220,7 @@ public class SpreadsheetCellBase implements SpreadsheetCell, EventTarget{ private static final int EDITABLE_BIT_POSITION = 4; private static final int WRAP_BIT_POSITION = 5; private static final int POPUP_BIT_POSITION = 6; + private static final int IS_BROWSER_POSITION = 7; private final SpreadsheetCellType type; private final int row; private final int column; @@ -348,7 +349,15 @@ public final void setEditable(boolean editable) { public boolean isWrapText(){ return isSet(WRAP_BIT_POSITION); } - + + public boolean isBrowser(){ + return isSet(IS_BROWSER_POSITION); + } + + public void setBrowser(boolean isBrowser){ + setMask(isBrowser, IS_BROWSER_POSITION); + } + /** {@inheritDoc} */ @Override public void setWrapText(boolean wrapText) { diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java index 2bc20a2ce..d8354a8ca 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013, 2018 ControlsFX + * Copyright (c) 2013, 2019 ControlsFX * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,9 +37,6 @@ import impl.org.controlsfx.spreadsheet.SpreadsheetGridView; import impl.org.controlsfx.spreadsheet.SpreadsheetHandle; import impl.org.controlsfx.spreadsheet.TableViewSpanSelectionModel; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.BitSet; import java.util.Comparator; @@ -420,6 +417,7 @@ public static enum SpanType { private Integer filteredRow; private FilteredList> filteredList; private SortedList> sortedList; + private BrowserInterface browser; /** * Since the default with applied to TableColumn is 80. If a user sets a @@ -492,6 +490,26 @@ public SpreadsheetView(){ column.setPrefWidth(100); } } + + /** + * Sets the BrowserInterface that will provide an implementation for cell + * that have {@link SpreadsheetCell#isBrowser() } set to {@code true}. + * + * @param browser the BrowserInterface + */ + public void setBrowser(BrowserInterface browser) { + this.browser = browser; + } + + /** + * Returns the BrowserInterface if set that provide implementation for + * browser in {@code SpreadsheetCell}. + * + * @return the BrowserInterface + */ + public BrowserInterface getBrowser() { + return browser; + } /** * Creates a SpreadsheetView control with the {@link Grid} specified. @@ -1906,12 +1924,7 @@ public void copyClipboard() { */ for (int row = 0; row < getRowSpan(cell, p.getRow()); ++row) { for (int col = 0; col < getColumnSpan(cell); ++col) { - try { - new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(cell.getItem()); - list.add(new ClipboardCell(p.getRow() + row, p.getColumn() + col, cell.getItem() == null ? null : cell.getItem())); - } catch (IOException exception) { - list.add(new ClipboardCell(p.getRow() + row, p.getColumn() + col, cell.getItem() == null ? null : cell.getItem().toString())); - } + list.add(new ClipboardCell(p.getRow() + row, p.getColumn() + col, cell)); } } } @@ -1927,7 +1940,7 @@ public void copyClipboard() { */ private void pasteOneValue(ClipboardCell change) { for (TablePosition position : getSelectionModel().getSelectedCells()) { - tryPasteCell(getModelRow(position.getRow()), getModelColumn(position.getColumn()), change.getValue()); + tryPasteCell(getModelRow(position.getRow()), getModelColumn(position.getColumn()), change); } } @@ -1937,10 +1950,14 @@ private void pasteOneValue(ClipboardCell change) { * @param column * @param value */ - private void tryPasteCell(int row, int column, Object value) { + private void tryPasteCell(int row, int column, ClipboardCell change) { final SpanType type = getSpanType(row, column); if (type == SpanType.NORMAL_CELL || type == SpanType.ROW_VISIBLE) { SpreadsheetCell cell = getGrid().getRows().get(row).get(column); + //Retrieve html in value if exists + //Value contains the HTML version or the nomrla value. + Object value = change.getHtmlVersion() == null ? change.getValue() : change.getHtmlVersion(); + value = cell.isBrowser() ? value : change.getValue(); boolean succeed = cell.getCellType().match(value, cell.getOptionsForEditor()); if (succeed) { getGrid().setCellValue(cell.getRow(), cell.getColumn(), @@ -1988,7 +2005,7 @@ private void pasteMixedValues(ArrayList list) { int modelColumn = getModelColumn(column); if (row < getGrid().getRowCount() && modelColumn < getGrid().getColumnCount() && row >= 0 && column >= 0) { - tryPasteCell(row, modelColumn, change.getValue()); + tryPasteCell(row, modelColumn, change); } } while ((column = column + sourceColumnGap) <= targetRange.getRight()); } @@ -2002,7 +2019,7 @@ private void pasteMixedValues(ArrayList list) { int modelRow = getModelRow(row); if (modelRow < getGrid().getRowCount() && column < getGrid().getColumnCount() && row >= 0 && column >= 0) { - tryPasteCell(modelRow, column, change.getValue()); + tryPasteCell(modelRow, column, change); } } while ((row = row + sourceRowGap) <= targetRange.getBottom()); } @@ -2053,7 +2070,7 @@ private void pasteSeveralValues(ArrayList list) { column = getModelColumn(change.getColumn() + offsetCol); if (row < rowCount && column < columnCount && row >= 0 && column >= 0) { - tryPasteCell(row, column, change.getValue()); + tryPasteCell(row, column, change); } } } From 5fb081975149907f6624769a8df2f0482a572430 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Mon, 24 Jun 2019 11:42:46 +0200 Subject: [PATCH 02/17] Fixing glitch with fixedColumns and rowSpan when horizontally scrolling --- .../controlsfx/spreadsheet/GridRowSkin.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java index 7d12666e1..b15fc5f2c 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java @@ -230,41 +230,38 @@ protected void layoutChildren(double x, final double y, final double w, final do * fixedColumnWidth will be wrong. */ boolean increaseFixedWidth = false; - final int viewColumn =spreadsheetView.getViewColumn(spreadsheetCell.getColumn()); + final int viewColumn = spreadsheetView.getViewColumn(spreadsheetCell.getColumn()); //Virtualization of column + SpreadsheetView.SpanType spanType = null; // We translate that column by the Hbar Value if it's fixed if (isFixed) { + spanType = spreadsheetView.getSpanType(index, indexColumn); /** - * Here we verify if our cell must be shifted. The second - * condition is to determine that we are dealing with the very - * first cell of a columnSpan. If we have the hidden cells, we - * must not increase the fixedColumnWidth. + * Here we verify if our cell must be shifted. We must increase + * all cells that are not column_span_invisible because their + * whole width will be computed by the original cell that is + * row_visible. */ - if (hbarValue + fixedColumnWidth > x && spreadsheetCell.getColumn() == indexColumn) { + if (spanType != SpreadsheetView.SpanType.COLUMN_SPAN_INVISIBLE && hbarValue + fixedColumnWidth > x) { increaseFixedWidth = true; tableCellX = Math.abs(hbarValue - x + fixedColumnWidth); -// tableCell.toFront(); fixedColumnWidth += width; -// isVisible = true; // If in fixedColumn, it's obviously visible fixedCells.add(tableCell); } } if (isVisible) { - final SpreadsheetView.SpanType spanType = spreadsheetView.getSpanType(index, indexColumn); - + spanType = spanType == null ? spreadsheetView.getSpanType(index, indexColumn) : spanType; switch (spanType) { case ROW_SPAN_INVISIBLE: case BOTH_INVISIBLE: fixedCells.remove(tableCell); getChildren().remove(tableCell); -// cells.remove(tableCell); x += width; continue; // we don't want to fall through case COLUMN_SPAN_INVISIBLE: fixedCells.remove(tableCell); getChildren().remove(tableCell); -// cells.remove(tableCell); continue; // we don't want to fall through case ROW_VISIBLE: case NORMAL_CELL: // fall through and carry on @@ -293,7 +290,7 @@ protected void layoutChildren(double x, final double y, final double w, final do * variable */ final int max = skin.getSkinnable().getVisibleLeafColumns().size() - viewColumn; - for (int i = 1, colSpan = columnSpan; i < colSpan && i < max; i++) { + for (int i = 1; i < columnSpan && i < max; i++) { double tempWidth = snapSize(skin.getSkinnable().getVisibleLeafColumn(viewColumn + i).getWidth()); width += tempWidth; if (increaseFixedWidth) { @@ -301,7 +298,7 @@ protected void layoutChildren(double x, final double y, final double w, final do } } } - + /** * If we are in autofit and the prefHeight of this cell is * superior to the default cell height. Then we will use this From 8f115425075d0e3ea9872351b1c145a05861d27d Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Mon, 15 Jul 2019 14:33:59 +0200 Subject: [PATCH 03/17] Adding more details for Cell graphic for Browser --- .../impl/org/controlsfx/spreadsheet/CellView.java | 2 +- .../control/spreadsheet/BrowserInterface.java | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java index 10ec7e495..5a700bf62 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java @@ -287,7 +287,7 @@ protected void layoutChildren() { BrowserInterface browserImpl = handle.getView().getBrowser(); //When layout is called, the Font has been set on the cell, we can give it to the browserInterface if (dirtyStyle && browserImpl != null && getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { - browserImpl.loadStyle(getGraphic(), getFont()); + browserImpl.loadStyle(getGraphic(), getFont(), getTextFill(), getAlignment(), getBackground()); dirtyStyle = false; } } diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java index 38413a767..695d4a129 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java @@ -26,7 +26,10 @@ */ package org.controlsfx.control.spreadsheet; +import javafx.geometry.Pos; import javafx.scene.Node; +import javafx.scene.layout.Background; +import javafx.scene.paint.Paint; import javafx.scene.text.Font; import javafx.scene.web.WebView; @@ -64,12 +67,16 @@ public interface BrowserInterface { /** * Once a {@code SpreadsheetCell} has been effectively loaded in the grid, - * this method is called if the browser wants to access the cell's font. + * this method is called if the browser wants to access the cell's graphic + * details. * - * @param browser the considered browsr - * @param font the cell font + * @param browser the considered browser + * @param font the cell {@code Font} + * @param textFill the text's color + * @param alignment the cell's vertical and horizontal alignment + * @param background the cell's background */ - public void loadStyle(T browser, Font font); + public void loadStyle(T browser, Font font, Paint textFill, Pos alignment, Background background); /** * Once a browser is no longer used in a cell, it is given back. From a2c3a0034b7b2150b9a9b5eef33244158e3df8e0 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Wed, 31 Jul 2019 15:24:49 +0200 Subject: [PATCH 04/17] Providing SpreadsheetCell directly to the Browser Interface --- .../samples/spreadsheet/BrowserImpl.java | 22 +++++++++++-------- .../org/controlsfx/spreadsheet/CellView.java | 11 +++++----- .../control/spreadsheet/BrowserInterface.java | 14 +++++++----- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java index 076ea6253..51fec6f45 100644 --- a/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java +++ b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java @@ -30,11 +30,15 @@ import java.util.LinkedList; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; +import javafx.scene.layout.Background; +import javafx.scene.paint.Paint; import javafx.scene.text.Font; import javafx.scene.web.WebView; import org.controlsfx.control.spreadsheet.BrowserInterface; +import org.controlsfx.control.spreadsheet.SpreadsheetCell; /** * @@ -48,18 +52,18 @@ public class BrowserImpl implements BrowserInterface { private final Map loadedUrl = new HashMap<>(); @Override - public Node getBrowser(Object url) { + public Node getBrowser(SpreadsheetCell cell) { //Limit the number of browser displayed. if (browserCounter.get() < 256) { if (browserList.isEmpty()) { browserCounter.incrementAndGet(); WebView webView = new WebView(); - webView.getEngine().loadContent(url.toString()); - loadedUrl.put(webView, url.toString()); + webView.getEngine().loadContent(cell.getItem().toString()); + loadedUrl.put(webView, cell.getItem().toString()); return webView; } else { WebView webView = browserList.pop(); - webView.getEngine().loadContent(url.toString()); + webView.getEngine().loadContent(cell.getItem().toString()); return webView; } } else { @@ -68,14 +72,14 @@ public Node getBrowser(Object url) { } @Override - public void load(WebView browser, Object url) { - if (!loadedUrl.containsKey(browser) || !loadedUrl.get(browser).equals(url)) { - browser.getEngine().loadContent(url.toString()); - loadedUrl.put(browser, url.toString()); + public void load(WebView browser, SpreadsheetCell cell) { + if (!loadedUrl.containsKey(browser) || !loadedUrl.get(browser).equals(cell.getItem())) { + browser.getEngine().loadContent(cell.getItem().toString()); + loadedUrl.put(browser, cell.getItem().toString()); } } - public void loadStyle(WebView browser, Font font) { + public void loadStyle(WebView browser, SpreadsheetCell cell, Font font, Paint textFill, Pos alignment, Background background) { //no-op } diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java index 5a700bf62..87d76d048 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java @@ -285,9 +285,10 @@ public void updateItem(final SpreadsheetCell item, boolean empty) { protected void layoutChildren() { super.layoutChildren(); BrowserInterface browserImpl = handle.getView().getBrowser(); - //When layout is called, the Font has been set on the cell, we can give it to the browserInterface - if (dirtyStyle && browserImpl != null && getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { - browserImpl.loadStyle(getGraphic(), getFont(), getTextFill(), getAlignment(), getBackground()); + //When layout is called, the cell style has been set on the cell, we can give it to the browserInterface + if (dirtyStyle && browserImpl != null && getItem().isBrowser()) { + //getGraphic may be null if the cell is empty + browserImpl.loadStyle(getGraphic(), getItem(), getFont(), getTextFill(), getAlignment(), getBackground()); dirtyStyle = false; } } @@ -402,9 +403,9 @@ private void setBrowserGraphic(SpreadsheetCell item) { textProperty().unbind(); setText(null); if (getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { - browserImpl.load(getGraphic(), item.getItem()); + browserImpl.load(getGraphic(), item); } else { - setGraphic(browserImpl.getBrowser(item.getItem())); + setGraphic(browserImpl.getBrowser(item)); } } diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java index 695d4a129..306abf5ba 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java @@ -49,10 +49,10 @@ public interface BrowserInterface { * internally by the SpreadsheetView when a cell is being visible and needs * to display a browser. * - * @param itemValue what is contained in {@link SpreadsheetCell#getItem() } + * @param cell the considered SpreadsheetCell * @return a {@code Node} to display in the cell graphic */ - public Node getBrowser(Object itemValue); + public Node getBrowser(SpreadsheetCell cell); /** * When a browser is reused (transfered from one cell to another for @@ -61,22 +61,24 @@ public interface BrowserInterface { * and itemValue. * * @param browser the considered browser - * @param itemValue what is contained in {@link SpreadsheetCell#getItem() } + * @param cell the considered SpreadsheetCell */ - public void load(T browser, Object itemValue); + public void load(T browser, SpreadsheetCell cell); /** * Once a {@code SpreadsheetCell} has been effectively loaded in the grid, * this method is called if the browser wants to access the cell's graphic * details. * - * @param browser the considered browser + * @param browser the considered browser, may be {@code null} for empty + * browser cell. + * @param cell the considered SpreadsheetCell * @param font the cell {@code Font} * @param textFill the text's color * @param alignment the cell's vertical and horizontal alignment * @param background the cell's background */ - public void loadStyle(T browser, Font font, Paint textFill, Pos alignment, Background background); + public void loadStyle(T browser, SpreadsheetCell cell, Font font, Paint textFill, Pos alignment, Background background); /** * Once a browser is no longer used in a cell, it is given back. From 0260d2ce748d5ef076aec336e33e940a104c377b Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Mon, 5 Aug 2019 11:07:18 +0200 Subject: [PATCH 05/17] Increment minimum scroll value for SpreadsheetView --- .../org/controlsfx/control/spreadsheet/SpreadsheetView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java index d8354a8ca..cb2721d82 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java @@ -404,7 +404,7 @@ public static enum SpanType { //Zoom for the SpreadsheetView. private DoubleProperty zoomFactor = new SimpleDoubleProperty(1); - private static final double MIN_ZOOM = 0.1; + private static final double MIN_ZOOM = 0.2; private static final double MAX_ZOOM = 2; private static final double STEP_ZOOM = 0.10; //The visible rows. From f9157cc0ba9429282fda027f163b5a0491aa1a18 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Tue, 6 Aug 2019 10:56:12 +0200 Subject: [PATCH 06/17] Fixing zoom incrementation althorithm --- .../control/spreadsheet/SpreadsheetView.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java index cb2721d82..6f8e23286 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java @@ -1111,21 +1111,23 @@ public final DoubleProperty zoomFactorProperty() { * */ public void incrementZoom() { - double newZoom = getZoomFactor(); - int prevValue = (int) ((newZoom - MIN_ZOOM) / STEP_ZOOM); - newZoom = (prevValue + 1) * STEP_ZOOM + MIN_ZOOM; - setZoomFactor(newZoom > MAX_ZOOM ? MAX_ZOOM : newZoom); + double newZoom = zoomFactor.getValue() + STEP_ZOOM; + newZoom *= 10; + newZoom = Math.floor((float) newZoom); + newZoom /= 10; + zoomFactor.setValue(newZoom > MAX_ZOOM ? MAX_ZOOM : newZoom); } /** - * Decrement the level of zoom by 0.10. It will block at 0.25.The base is 1 + * Decrement the level of zoom by 0.10. It will block at 0.20. The base is 1 * so we will try to stay of the intervals. */ public void decrementZoom() { - double newZoom = getZoomFactor() - 0.01; - int prevValue = (int) ((newZoom - MIN_ZOOM) / STEP_ZOOM); - newZoom = (prevValue) * STEP_ZOOM + MIN_ZOOM; - setZoomFactor(newZoom < MIN_ZOOM ? MIN_ZOOM : newZoom); + double newZoom = zoomFactor.getValue() - STEP_ZOOM; + newZoom *= 10; + newZoom = Math.ceil((float) newZoom); + newZoom /= 10; + zoomFactor.setValue(newZoom < MIN_ZOOM ? MIN_ZOOM : newZoom); } /** From 16ac28d7c6f551aa4fd5db04edbd87319ac518a5 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Thu, 8 Aug 2019 16:19:47 +0200 Subject: [PATCH 07/17] WebView size considered for SpreadsheetView. --- .../org/controlsfx/spreadsheet/CellViewSkin.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellViewSkin.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellViewSkin.java index 0a54c844f..c47301b78 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellViewSkin.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellViewSkin.java @@ -45,7 +45,6 @@ import javafx.scene.control.TableColumn; import javafx.scene.image.ImageView; import javafx.scene.layout.Region; -import javafx.scene.web.WebView; import org.controlsfx.control.spreadsheet.Filter; import org.controlsfx.control.spreadsheet.SpreadsheetCell; import org.controlsfx.control.spreadsheet.SpreadsheetCell.CornerPosition; @@ -98,11 +97,6 @@ public CellViewSkin(CellView tableCell) { @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { Node graphic = getSkinnable().getGraphic(); - //WebView resize is not working so we do not consider it right now - if (graphic instanceof WebView) { - //WebView must not impact autofit - return -1; - } /** * If we have an Image in the Cell, its fitHeight will be affected by * the cell height (see CellView). But during calculation for autofit @@ -126,12 +120,6 @@ protected double computePrefHeight(double width, double topInset, double rightIn @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { - Node graphic = getSkinnable().getGraphic(); - //WebView resize is not working so we do not consider it right now - if(graphic instanceof WebView){ - //WebView must not impact autofit - return -1; - } /** * We integrate the filter width into the total width for autosize. We * do just like Excel. From 3db99af2d87de37771adfe729250d1dbc50984ce Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Tue, 27 Aug 2019 14:20:29 +0200 Subject: [PATCH 08/17] Avoid IndexOutOfBounds exception in RectangleSelection --- .../impl/org/controlsfx/spreadsheet/RectangleSelection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/RectangleSelection.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/RectangleSelection.java index e43390114..640d7e8fa 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/RectangleSelection.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/RectangleSelection.java @@ -254,7 +254,8 @@ private void handleHorizontalPositioning(int minColumn, int maxColumn) { * translate the starting point in because the rectangle must also be * hidden by the fixed column. */ - if (!skin.spreadsheetView.getFixedColumns().contains(columns.get(skin.spreadsheetView.getModelColumn(minColumn)))) { + int col = skin.spreadsheetView.getModelColumn(minColumn); + if (col < columns.size() && !skin.spreadsheetView.getFixedColumns().contains(columns.get(col))) { if (x < skin.fixedColumnWidth) { //Since I translate the starting point, I must reduce the width by the value I'm translating. width -= skin.fixedColumnWidth - x; From ab443a2b72c3349f16f14c9372124de024d84c65 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Mon, 16 Sep 2019 15:27:20 +0200 Subject: [PATCH 09/17] Renaming BrowserInterface to CellGraphicFactory and only send in loadStyle null or the proper Type of the interface --- .../samples/spreadsheet/BrowserImpl.java | 16 +++--- .../org/controlsfx/spreadsheet/CellView.java | 29 +++++------ ...Interface.java => CellGraphicFactory.java} | 49 ++++++++++--------- .../control/spreadsheet/ClipboardCell.java | 4 +- .../control/spreadsheet/SpreadsheetCell.java | 21 ++++---- .../spreadsheet/SpreadsheetCellBase.java | 4 +- .../control/spreadsheet/SpreadsheetView.java | 24 ++++----- 7 files changed, 76 insertions(+), 71 deletions(-) rename controlsfx/src/main/java/org/controlsfx/control/spreadsheet/{BrowserInterface.java => CellGraphicFactory.java} (65%) diff --git a/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java index 51fec6f45..4211634ab 100644 --- a/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java +++ b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java @@ -37,33 +37,33 @@ import javafx.scene.paint.Paint; import javafx.scene.text.Font; import javafx.scene.web.WebView; -import org.controlsfx.control.spreadsheet.BrowserInterface; import org.controlsfx.control.spreadsheet.SpreadsheetCell; +import org.controlsfx.control.spreadsheet.CellGraphicFactory; /** * * Example of implementation of the BrowserInterface in order to provide WebView * inside of cells. */ -public class BrowserImpl implements BrowserInterface { +public class BrowserImpl implements CellGraphicFactory { private final LinkedList browserList = new LinkedList<>(); private final AtomicInteger browserCounter = new AtomicInteger(); private final Map loadedUrl = new HashMap<>(); @Override - public Node getBrowser(SpreadsheetCell cell) { + public Node getNode(SpreadsheetCell cell) { //Limit the number of browser displayed. if (browserCounter.get() < 256) { if (browserList.isEmpty()) { browserCounter.incrementAndGet(); WebView webView = new WebView(); - webView.getEngine().loadContent(cell.getItem().toString()); + webView.getEngine().loadContent( cell.getItem().toString()); loadedUrl.put(webView, cell.getItem().toString()); return webView; } else { WebView webView = browserList.pop(); - webView.getEngine().loadContent(cell.getItem().toString()); + webView.getEngine().load(cell.getItem().toString()); return webView; } } else { @@ -74,20 +74,22 @@ public Node getBrowser(SpreadsheetCell cell) { @Override public void load(WebView browser, SpreadsheetCell cell) { if (!loadedUrl.containsKey(browser) || !loadedUrl.get(browser).equals(cell.getItem())) { - browser.getEngine().loadContent(cell.getItem().toString()); + browser.getEngine().load(cell.getItem().toString()); loadedUrl.put(browser, cell.getItem().toString()); } } + @Override public void loadStyle(WebView browser, SpreadsheetCell cell, Font font, Paint textFill, Pos alignment, Background background) { //no-op } @Override - public void setUnusedBrowser(WebView browser) { + public void setUnusedNode(WebView browser) { browserList.add(browser); } + @Override public Class getType() { return WebView.class; } diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java index 87d76d048..c4504fb34 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java @@ -66,7 +66,7 @@ import javafx.beans.value.ObservableValue; import javafx.scene.Node; import javafx.scene.layout.Region; -import org.controlsfx.control.spreadsheet.BrowserInterface; +import org.controlsfx.control.spreadsheet.CellGraphicFactory; /** * @@ -268,14 +268,14 @@ public void updateItem(final SpreadsheetCell item, boolean empty) { textProperty().unbind(); setText(null); //Release any browser we might have - BrowserInterface browserImpl = handle.getView().getBrowser(); + CellGraphicFactory browserImpl = handle.getView().getCellGraphicFactory(); if (browserImpl != null && getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { - browserImpl.setUnusedBrowser(getGraphic()); + browserImpl.setUnusedNode(getGraphic()); setGraphic(null); } } else if (!isEditing() && item != null) { show(item); - if (item.getGraphic() == null && !item.isBrowser()) { + if (item.getGraphic() == null && !item.isCellGraphic()) { setGraphic(null); } } @@ -284,11 +284,12 @@ public void updateItem(final SpreadsheetCell item, boolean empty) { @Override protected void layoutChildren() { super.layoutChildren(); - BrowserInterface browserImpl = handle.getView().getBrowser(); - //When layout is called, the cell style has been set on the cell, we can give it to the browserInterface - if (dirtyStyle && browserImpl != null && getItem().isBrowser()) { - //getGraphic may be null if the cell is empty - browserImpl.loadStyle(getGraphic(), getItem(), getFont(), getTextFill(), getAlignment(), getBackground()); + CellGraphicFactory browserImpl = handle.getView().getCellGraphicFactory(); + //When layout is called, the cell style has been set on the cell, we can give it to the CellGraphicFactory + if (dirtyStyle && browserImpl != null && getItem().isCellGraphic()) { + //Send the graphic only if it's the right type, otherwise send null. + Node node = getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass()) ? getGraphic() : null; + browserImpl.loadStyle(node, getItem(), getFont(), getTextFill(), getAlignment(), getBackground()); dirtyStyle = false; } } @@ -396,7 +397,7 @@ public boolean isOriginalCell() { * @param item */ private void setBrowserGraphic(SpreadsheetCell item) { - BrowserInterface browserImpl = handle.getView().getBrowser(); + CellGraphicFactory browserImpl = handle.getView().getCellGraphicFactory(); if (browserImpl == null) { return; } @@ -405,7 +406,7 @@ private void setBrowserGraphic(SpreadsheetCell item) { if (getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { browserImpl.load(getGraphic(), item); } else { - setGraphic(browserImpl.getBrowser(item)); + setGraphic(browserImpl.getNode(item)); } } @@ -422,11 +423,11 @@ private void setCellGraphic(SpreadsheetCell item) { if (isEditing()) { return; } - if (item.isBrowser() && item.getItem() != null) { + if (item.isCellGraphic() && item.getItem() != null) { setBrowserGraphic(item); return; - } else if (getGraphic() != null && handle.getView().getBrowser() != null && handle.getView().getBrowser().getType().isAssignableFrom(getGraphic().getClass())) { - handle.getView().getBrowser().setUnusedBrowser(getGraphic()); + } else if (getGraphic() != null && handle.getView().getCellGraphicFactory() != null && handle.getView().getCellGraphicFactory().getType().isAssignableFrom(getGraphic().getClass())) { + handle.getView().getCellGraphicFactory().setUnusedNode(getGraphic()); setGraphic(null); } Node graphic = item.getGraphic(); diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/CellGraphicFactory.java similarity index 65% rename from controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java rename to controlsfx/src/main/java/org/controlsfx/control/spreadsheet/CellGraphicFactory.java index 306abf5ba..2aaba277c 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/BrowserInterface.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/CellGraphicFactory.java @@ -35,64 +35,65 @@ /** * - * If anyone wants to display Rich Text in a SpreadsheetCell, a solution is to - * provide a Browser (for example a {@link WebView}) that will be displayed in - * the cell. + * If anyone wants to display a specific Graphic in a SpreadsheetCell, a + * solution is to provide a Node (for example a {@link WebView}) that will be + * displayed in the cell. * - * Because a browser consumes a lot of memory, we need this BrowserInterface - * that will recycle the Browser to only provide them for visible cells. + * Because a Node can consume a lot of memory, we need this + * {@code CellGraphicFactory} that will recycle the Nodes to only provide them + * for visible cells. */ -public interface BrowserInterface { +public interface CellGraphicFactory { /** * Returns a {@code Node} to display in the cell graphic. This is called * internally by the SpreadsheetView when a cell is being visible and needs - * to display a browser. + * to display a {@code Node}. * * @param cell the considered SpreadsheetCell * @return a {@code Node} to display in the cell graphic */ - public Node getBrowser(SpreadsheetCell cell); + public Node getNode(SpreadsheetCell cell); /** - * When a browser is reused (transfered from one cell to another for - * example), we ask the browser to reload. Beware, only reload when - * necessary! This method can be called several times with the same browser - * and itemValue. + * When a {@code Node} is reused (transfered from one cell to another for + * example), we ask the Node to reload. Beware, only reload when necessary! + * This method can be called several times with the same {@code Node} and + * itemValue. * - * @param browser the considered browser + * @param node the considered {@code Node} * @param cell the considered SpreadsheetCell */ - public void load(T browser, SpreadsheetCell cell); + public void load(T node, SpreadsheetCell cell); /** * Once a {@code SpreadsheetCell} has been effectively loaded in the grid, - * this method is called if the browser wants to access the cell's graphic - * details. + * this method is called if the {@code Node} wants to access the cell's + * graphic details. * - * @param browser the considered browser, may be {@code null} for empty - * browser cell. + * @param node the considered {@code Node}, may be {@code null} for empty + * cell. * @param cell the considered SpreadsheetCell * @param font the cell {@code Font} * @param textFill the text's color * @param alignment the cell's vertical and horizontal alignment * @param background the cell's background */ - public void loadStyle(T browser, SpreadsheetCell cell, Font font, Paint textFill, Pos alignment, Background background); + public void loadStyle(T node, SpreadsheetCell cell, Font font, Paint textFill, Pos alignment, Background background); /** - * Once a browser is no longer used in a cell, it is given back. + * Once a {@code Node} is no longer used in a cell, it is given back. * - * @param browser the browser + * @param node the {@code Node} */ - public void setUnusedBrowser(T browser); + public void setUnusedNode(T node); /** - * Returns the exact class used in this BrowserInterface. It is used to + * Returns the exact class used in this CellGraphicFactory. It is used to * determine if the cell's graphic is handled by the cell of by this * interface. * - * @return the exact class used in this BrowserInterface + * @return the exact class used in this CellGraphicFactory */ public Class getType(); diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/ClipboardCell.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/ClipboardCell.java index b4867e924..04cbcfc9d 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/ClipboardCell.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/ClipboardCell.java @@ -61,7 +61,7 @@ public ClipboardCell(int row, int column, SpreadsheetCell spc) { this.row = row; this.column = column; Object value = spc.getItem(); - if (spc.isBrowser()) { + if (spc.isCellGraphic()) { this.htmlVersion = value == null ? null : value.toString(); //Trust the SpreadsheetCellType to return a proper String version of the HTML this.value = spc.getCellType().toString(spc.getItem()); @@ -104,7 +104,7 @@ public Object getValue() { } /** - * If the original cell had its {@link SpreadsheetCell#isBrowser() } to + * If the original cell had its {@link SpreadsheetCell#isCellGraphic() } to * {@code true}, the cell HTML content will be here and the string version * in {@link #getValue() }. * diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java index ae9465436..f662b3e9d 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java @@ -140,23 +140,24 @@ public static enum CornerPosition { public boolean isWrapText(); /** - * Returns {@code true} if this cell contains HTML (URL or pure HTML) in its - * item and a browser given by the {@link BrowserInterface} will be used to + * Returns {@code true} if this cell contains something particular in its + * item and a Node given by the {@link CellGraphicFactory} will be used to * display it. * - * @return {@code true} if this cell contains HTML + * @return {@code true} if this cell item needs to be given to a particular + * Node */ - public boolean isBrowser(); + public boolean isCellGraphic(); /** - * If {@code isBrowser} is {@code true}, this cell item contains some HTML - * and should be display by using {@link BrowserInterface} object in the - * CellView. + * If {@code isCellGraphic} is {@code true}, this cell item contains + * something particular and should be display by using + * {@link CellGraphicFactory} object in the CellView. * - * @param isBrowser if {@code true}, a browser will be used to display HTML - * for the cell + * @param isCellGraphic if {@code true}, a Node will be used to display + * something particular for the cell */ - public void setBrowser(boolean isBrowser); + public void setCellGraphic(boolean isCellGraphic); /** * If a run of text exceeds the width of the Labeled, then this variable diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java index de8e3c5fa..23b8d4b80 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java @@ -350,11 +350,11 @@ public boolean isWrapText(){ return isSet(WRAP_BIT_POSITION); } - public boolean isBrowser(){ + public boolean isCellGraphic(){ return isSet(IS_BROWSER_POSITION); } - public void setBrowser(boolean isBrowser){ + public void setCellGraphic(boolean isBrowser){ setMask(isBrowser, IS_BROWSER_POSITION); } diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java index 6f8e23286..6dc3a2141 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetView.java @@ -417,7 +417,7 @@ public static enum SpanType { private Integer filteredRow; private FilteredList> filteredList; private SortedList> sortedList; - private BrowserInterface browser; + private CellGraphicFactory cellGraphicFactory; /** * Since the default with applied to TableColumn is 80. If a user sets a @@ -492,23 +492,23 @@ public SpreadsheetView(){ } /** - * Sets the BrowserInterface that will provide an implementation for cell - * that have {@link SpreadsheetCell#isBrowser() } set to {@code true}. + * Sets the CellGraphicFactory that will provide an implementation for cell + * that have {@link SpreadsheetCell#isCellGraphic() } set to {@code true}. * - * @param browser the BrowserInterface + * @param cellGraphicFactory the CellGraphicFactory */ - public void setBrowser(BrowserInterface browser) { - this.browser = browser; + public void setCellGraphicFactory(CellGraphicFactory cellGraphicFactory) { + this.cellGraphicFactory = cellGraphicFactory; } - + /** - * Returns the BrowserInterface if set that provide implementation for + * Returns the CellGraphicFactory if set that provide implementation for * browser in {@code SpreadsheetCell}. * - * @return the BrowserInterface + * @return the CellGraphicFactory */ - public BrowserInterface getBrowser() { - return browser; + public CellGraphicFactory getCellGraphicFactory() { + return cellGraphicFactory; } /** @@ -1959,7 +1959,7 @@ private void tryPasteCell(int row, int column, ClipboardCell change) { //Retrieve html in value if exists //Value contains the HTML version or the nomrla value. Object value = change.getHtmlVersion() == null ? change.getValue() : change.getHtmlVersion(); - value = cell.isBrowser() ? value : change.getValue(); + value = cell.isCellGraphic() ? value : change.getValue(); boolean succeed = cell.getCellType().match(value, cell.getOptionsForEditor()); if (succeed) { getGrid().setCellValue(cell.getRow(), cell.getColumn(), From 4cedc2cb67b71f39a5b6ef399c5824a81fcff0b6 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Mon, 16 Sep 2019 15:27:55 +0200 Subject: [PATCH 10/17] Fix to avoid constant layout when a webview is in fixed rows and columns and we scroll a bit horizontally. --- .../controlsfx/spreadsheet/GridRowSkin.java | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java index b15fc5f2c..364fbeac5 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java @@ -183,6 +183,7 @@ protected void layoutChildren(double x, final double y, final double w, final do boolean needToBeShifted; boolean rowHeightChange = false; boolean isFixed; + GridRow topRow = null; for (int indexColumn = 0; indexColumn < columns.size(); indexColumn++) { //FIXME Problem qwith column span if(!skin.getSkinnable().getColumns().get(indexColumn).isVisible()){ @@ -243,6 +244,8 @@ protected void layoutChildren(double x, final double y, final double w, final do * row_visible. */ if (spanType != SpreadsheetView.SpanType.COLUMN_SPAN_INVISIBLE && hbarValue + fixedColumnWidth > x) { + //We will need it later + topRow = topRow == null ? skin.getFlow().getTopRow() : topRow; increaseFixedWidth = true; tableCellX = Math.abs(hbarValue - x + fixedColumnWidth); fixedColumnWidth += width; @@ -279,7 +282,17 @@ protected void layoutChildren(double x, final double y, final double w, final do * getSkinnable because of the deportedCells. */ if (!tableCell.isEditing() && tableCell.getParent() != getSkinnable()) { - getChildren().add(0, tableCell); + /** + * If the considered cell is fixed and already + * contained in the topRow, no need to put it in + * this row, then put in the topRow right after. + * Prevent constant layout when WebView are + * contained in frozen row and columns, and + * horizontal scollbar is scrolled. + */ + if (topRow == null || !fixedCells.contains(tableCell) || !skin.deportedCells.containsKey(topRow) || !skin.deportedCells.get(topRow).contains(tableCell)) { + getChildren().add(0, tableCell); + } } } @@ -394,7 +407,7 @@ protected void layoutChildren(double x, final double y, final double w, final do x += width; } skin.fixedColumnWidth = fixedColumnWidth; - handleFixedCell(fixedCells, index); + handleFixedCell(fixedCells, index, topRow); removeUselessCell(index); if (handle.getCellsViewSkin().lastRowLayout.get() == true) { handle.getCellsViewSkin().lastRowLayout.setValue(false); @@ -466,7 +479,7 @@ private void removeDeportedCells() { * @param fixedCells * @param index */ - private void handleFixedCell(List fixedCells, int index) { + private void handleFixedCell(List fixedCells, int index, GridRow topRow) { removeDeportedCells(); if (fixedCells.isEmpty()) { return; @@ -478,21 +491,20 @@ private void handleFixedCell(List fixedCells, int index) { * we need to put it in another row. */ if (skin.rowToLayout.get(index)) { - GridRow gridRow = skin.getFlow().getTopRow(); - if (gridRow != null) { + if (topRow != null) { for (CellView cell : fixedCells) { if (!cell.isEditing()) { - gridRow.removeCell(cell); - gridRow.addCell(cell); + topRow.removeCell(cell); + topRow.addCell(cell); } final double originalLayoutY = getSkinnable().getLayoutY() + cell.getLayoutY(); - if (skin.deportedCells.containsKey(gridRow)) { - skin.deportedCells.get(gridRow).add(cell); + if (skin.deportedCells.containsKey(topRow)) { + skin.deportedCells.get(topRow).add(cell); } else { Set temp = new HashSet<>(); temp.add(cell); - skin.deportedCells.put(gridRow, temp); + skin.deportedCells.put(topRow, temp); } /** * I need to have the layoutY of the original row, but also @@ -501,7 +513,7 @@ private void handleFixedCell(List fixedCells, int index) { * translate in order to be visible, we need to remove that * "bit of translate". */ - cell.relocate(cell.getLayoutX(), originalLayoutY - gridRow.getLayoutY()); + cell.relocate(cell.getLayoutX(), originalLayoutY - topRow.getLayoutY()); } } } else { From 7f9a4f4a883f11427de894cffbc1e7312feb0a6b Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Wed, 18 Sep 2019 17:49:11 +0200 Subject: [PATCH 11/17] Work-around in order to release as much as we can cell graphic. It seems very difficult to completly know when a TableCell is out of the system. --- .../org/controlsfx/spreadsheet/CellView.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java index c4504fb34..2f6a0d6fe 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java @@ -65,6 +65,7 @@ import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.scene.Node; +import javafx.scene.Parent; import javafx.scene.layout.Region; import org.controlsfx.control.spreadsheet.CellGraphicFactory; @@ -124,6 +125,14 @@ public CellView(SpreadsheetHandle handle) { setOnMouseDragEntered(new WeakEventHandler<>(dragMouseEventHandler)); itemProperty().addListener(itemChangeListener); + parentProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Parent oldValue, Parent newParent) { + if(newParent == null && getGraphic() != null){ + releaseCellGraphic(); + } + } + }); } /*************************************************************************** @@ -268,11 +277,7 @@ public void updateItem(final SpreadsheetCell item, boolean empty) { textProperty().unbind(); setText(null); //Release any browser we might have - CellGraphicFactory browserImpl = handle.getView().getCellGraphicFactory(); - if (browserImpl != null && getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { - browserImpl.setUnusedNode(getGraphic()); - setGraphic(null); - } + releaseCellGraphic(); } else if (!isEditing() && item != null) { show(item); if (item.getGraphic() == null && !item.isCellGraphic()) { @@ -281,6 +286,20 @@ public void updateItem(final SpreadsheetCell item, boolean empty) { } } + /** + * Releases and remove the graphic if any has been defined. Useful when the + * graphic comes from the {@link SpreadsheetView#getCellGraphicFactory() } + * and explicitly needs to be given back. + */ + public void releaseCellGraphic() { + //Release any browser we might have + CellGraphicFactory browserImpl = handle.getView().getCellGraphicFactory(); + if (browserImpl != null && getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { + browserImpl.setUnusedNode(getGraphic()); + setGraphic(null); + } + } + @Override protected void layoutChildren() { super.layoutChildren(); @@ -426,9 +445,8 @@ private void setCellGraphic(SpreadsheetCell item) { if (item.isCellGraphic() && item.getItem() != null) { setBrowserGraphic(item); return; - } else if (getGraphic() != null && handle.getView().getCellGraphicFactory() != null && handle.getView().getCellGraphicFactory().getType().isAssignableFrom(getGraphic().getClass())) { - handle.getView().getCellGraphicFactory().setUnusedNode(getGraphic()); - setGraphic(null); + } else if (getGraphic() != null) { + releaseCellGraphic(); } Node graphic = item.getGraphic(); if (graphic != null) { From f5a3ae7abf749f4132bfa9c1bba1d47f44cafe11 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Thu, 19 Sep 2019 15:28:10 +0200 Subject: [PATCH 12/17] Only add the parentProperty listener when cell has a graphic and not fixed to avoid constant layout. --- .../org/controlsfx/spreadsheet/CellView.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java index 2f6a0d6fe..97210ef67 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java @@ -125,14 +125,6 @@ public CellView(SpreadsheetHandle handle) { setOnMouseDragEntered(new WeakEventHandler<>(dragMouseEventHandler)); itemProperty().addListener(itemChangeListener); - parentProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Parent oldValue, Parent newParent) { - if(newParent == null && getGraphic() != null){ - releaseCellGraphic(); - } - } - }); } /*************************************************************************** @@ -297,6 +289,7 @@ public void releaseCellGraphic() { if (browserImpl != null && getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { browserImpl.setUnusedNode(getGraphic()); setGraphic(null); + parentProperty().removeListener(parentListener); } } @@ -426,6 +419,10 @@ private void setBrowserGraphic(SpreadsheetCell item) { browserImpl.load(getGraphic(), item); } else { setGraphic(browserImpl.getNode(item)); + //If row or column is fixed, don't try to remove the graphic because constant layout can happen + if (!handle.getView().getFixedRows().contains(getIndex()) && !handle.getView().getFixedColumns().contains(item.getColumn())) { + parentProperty().addListener(parentListener); + } } } @@ -771,6 +768,18 @@ public void handle(MouseEvent event) { }; private final WeakEventHandler weakActionhandler = new WeakEventHandler(actionEventHandler); + /** + * When this cell looses its parent, we release the Graphic if we had one. + */ + private ChangeListener parentListener = new ChangeListener() { + @Override + public void changed(ObservableValue observable, Parent oldValue, Parent newParent) { + if (newParent == null && getGraphic() != null) { + releaseCellGraphic(); + } + } + }; + private void initStyleListener(){ if(styleListener == null){ styleListener = (ObservableValue observable, String oldValue, String newValue) -> { From b99b6b33f5b04484eba9fd7502a53081d2f9b233 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Mon, 23 Sep 2019 11:24:55 +0200 Subject: [PATCH 13/17] Set dirtyStyle to true only for SpreadsheetCell with cellgraphic activated and also activates it when setting the cellgraphic. --- .../org/controlsfx/spreadsheet/CellView.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java index 97210ef67..9260d40e2 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/CellView.java @@ -85,7 +85,7 @@ public class CellView extends TableCell, Spreads //Handler for drag n drop in lazy instantiation. private EventHandler dragOverHandler; private EventHandler dragDropHandler; - //Set to true when the style has changed + //Set to true when the style has changed, and the item has a cellgraphic. private boolean dirtyStyle = false; /*************************************************************************** @@ -296,13 +296,15 @@ public void releaseCellGraphic() { @Override protected void layoutChildren() { super.layoutChildren(); - CellGraphicFactory browserImpl = handle.getView().getCellGraphicFactory(); //When layout is called, the cell style has been set on the cell, we can give it to the CellGraphicFactory - if (dirtyStyle && browserImpl != null && getItem().isCellGraphic()) { - //Send the graphic only if it's the right type, otherwise send null. - Node node = getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass()) ? getGraphic() : null; - browserImpl.loadStyle(node, getItem(), getFont(), getTextFill(), getAlignment(), getBackground()); - dirtyStyle = false; + if (dirtyStyle && getItem().isCellGraphic()) { + CellGraphicFactory browserImpl = handle.getView().getCellGraphicFactory(); + if (browserImpl != null) { + //Send the graphic only if it's the right type, otherwise send null. + Node node = getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass()) ? getGraphic() : null; + browserImpl.loadStyle(node, getItem(), getFont(), getTextFill(), getAlignment(), getBackground()); + dirtyStyle = false; + } } } @@ -406,7 +408,8 @@ public boolean isOriginalCell() { /** * Sets the browser in the cell. - * @param item + * + * @param item */ private void setBrowserGraphic(SpreadsheetCell item) { CellGraphicFactory browserImpl = handle.getView().getCellGraphicFactory(); @@ -418,6 +421,8 @@ private void setBrowserGraphic(SpreadsheetCell item) { if (getGraphic() != null && browserImpl.getType().isAssignableFrom(getGraphic().getClass())) { browserImpl.load(getGraphic(), item); } else { + //In any case, the WebView will need to update its style to match the cell. + dirtyStyle = true; setGraphic(browserImpl.getNode(item)); //If row or column is fixed, don't try to remove the graphic because constant layout can happen if (!handle.getView().getFixedRows().contains(getIndex()) && !handle.getView().getFixedColumns().contains(item.getColumn())) { @@ -590,7 +595,9 @@ public void onChanged(javafx.collections.SetChangeListener.Change arg0, Spreadsheet //We clear the previous style. setStyle(null); } - dirtyStyle = true; + if (getItem() != null && getItem().isCellGraphic()) { + dirtyStyle = true; + } } } }; @@ -784,7 +793,9 @@ private void initStyleListener(){ if(styleListener == null){ styleListener = (ObservableValue observable, String oldValue, String newValue) -> { styleProperty().set(newValue); - dirtyStyle = true; + if (getItem() != null && getItem().isCellGraphic()) { + dirtyStyle = true; + } }; } weakStyleListener = new WeakChangeListener<>(styleListener); From 8a708b7d0fb5512fcb16a5822be43daf23daf140 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Tue, 24 Sep 2019 12:36:20 +0200 Subject: [PATCH 14/17] Including FixedRow when testing TableRow existence in VirtualFlow --- .../spreadsheet/GridVirtualFlow.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridVirtualFlow.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridVirtualFlow.java index 74e6185ab..e5240cfa8 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridVirtualFlow.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridVirtualFlow.java @@ -333,22 +333,21 @@ protected void layoutTotal() { } } + /** + * When we layout, we also remove the cell that have been deported into + * other rows in order not to have some TableCell hanging out. + * + * When scrolling with mouse wheel, we will request the layout of all rows, + * but only one row will be really called. Thus by wiping entirely the + * deportedCell, all cells in fixedColumns are gone. So we must be smarter. + */ private void removeDeportedCells() { - /** - * When we layout, we also remove the cell that have been deported into - * other rows in order not to have some TableCell hanging out. - * - * When scrolling with mouse wheel, we will request the layout of all - * rows, but only one row will be really called. Thus by wiping entirely - * the deportedCell, all cells in fixedColumns are gone. So we must be - * smarter. - */ ArrayList rowToRemove = new ArrayList<>(); for (Entry> entry : gridViewSkin.deportedCells.entrySet()) { ArrayList toRemove = new ArrayList<>(); for (CellView cell : entry.getValue()) { //If we're not editing and the TableRow of the cell is not contained anymore, we remove. - if (!cell.isEditing() && !getCells().contains(cell.getTableRow())) { + if (!cell.isEditing() && !getCells().contains(cell.getTableRow()) && !myFixedCells.contains(cell.getTableRow())) { entry.getKey().removeCell(cell); toRemove.add(cell); } From 3b278e8243c1f3cc93d30754d9403647c0bc3367 Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Wed, 25 Sep 2019 12:47:28 +0200 Subject: [PATCH 15/17] Use exact condition used by fixedCell when deciding if TableCell should be added or not in its GridRow --- .../java/impl/org/controlsfx/spreadsheet/GridRowSkin.java | 7 +++---- .../impl/org/controlsfx/spreadsheet/GridVirtualFlow.java | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java index 364fbeac5..778d7dae3 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java @@ -283,14 +283,13 @@ protected void layoutChildren(double x, final double y, final double w, final do */ if (!tableCell.isEditing() && tableCell.getParent() != getSkinnable()) { /** - * If the considered cell is fixed and already - * contained in the topRow, no need to put it in - * this row, then put in the topRow right after. + * If this cell is eligible to be put into the + * deportedCell, no need to add it in this row. * Prevent constant layout when WebView are * contained in frozen row and columns, and * horizontal scollbar is scrolled. */ - if (topRow == null || !fixedCells.contains(tableCell) || !skin.deportedCells.containsKey(topRow) || !skin.deportedCells.get(topRow).contains(tableCell)) { + if (!skin.rowToLayout.get(index) || topRow == null || !fixedCells.contains(tableCell)) { getChildren().add(0, tableCell); } } diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridVirtualFlow.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridVirtualFlow.java index e5240cfa8..a0de9eaa6 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridVirtualFlow.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridVirtualFlow.java @@ -347,7 +347,7 @@ private void removeDeportedCells() { ArrayList toRemove = new ArrayList<>(); for (CellView cell : entry.getValue()) { //If we're not editing and the TableRow of the cell is not contained anymore, we remove. - if (!cell.isEditing() && !getCells().contains(cell.getTableRow()) && !myFixedCells.contains(cell.getTableRow())) { + if (!cell.isEditing() && !getCells().contains(cell.getTableRow())) { entry.getKey().removeCell(cell); toRemove.add(cell); } From 9a3d5734628eae1e2fa1a846aba7dec0eae627ce Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Wed, 25 Sep 2019 17:29:04 +0200 Subject: [PATCH 16/17] Adding normal cell to gridRow children like before to not impact layout --- .../impl/org/controlsfx/spreadsheet/GridRowSkin.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java index 778d7dae3..b798766aa 100644 --- a/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java +++ b/controlsfx/src/main/java/impl/org/controlsfx/spreadsheet/GridRowSkin.java @@ -183,7 +183,6 @@ protected void layoutChildren(double x, final double y, final double w, final do boolean needToBeShifted; boolean rowHeightChange = false; boolean isFixed; - GridRow topRow = null; for (int indexColumn = 0; indexColumn < columns.size(); indexColumn++) { //FIXME Problem qwith column span if(!skin.getSkinnable().getColumns().get(indexColumn).isVisible()){ @@ -244,8 +243,6 @@ protected void layoutChildren(double x, final double y, final double w, final do * row_visible. */ if (spanType != SpreadsheetView.SpanType.COLUMN_SPAN_INVISIBLE && hbarValue + fixedColumnWidth > x) { - //We will need it later - topRow = topRow == null ? skin.getFlow().getTopRow() : topRow; increaseFixedWidth = true; tableCellX = Math.abs(hbarValue - x + fixedColumnWidth); fixedColumnWidth += width; @@ -289,7 +286,7 @@ protected void layoutChildren(double x, final double y, final double w, final do * contained in frozen row and columns, and * horizontal scollbar is scrolled. */ - if (!skin.rowToLayout.get(index) || topRow == null || !fixedCells.contains(tableCell)) { + if (!spreadsheetCell.isCellGraphic() || !skin.rowToLayout.get(index) || !fixedCells.contains(tableCell)) { getChildren().add(0, tableCell); } } @@ -406,7 +403,7 @@ protected void layoutChildren(double x, final double y, final double w, final do x += width; } skin.fixedColumnWidth = fixedColumnWidth; - handleFixedCell(fixedCells, index, topRow); + handleFixedCell(fixedCells, index); removeUselessCell(index); if (handle.getCellsViewSkin().lastRowLayout.get() == true) { handle.getCellsViewSkin().lastRowLayout.setValue(false); @@ -478,7 +475,7 @@ private void removeDeportedCells() { * @param fixedCells * @param index */ - private void handleFixedCell(List fixedCells, int index, GridRow topRow) { + private void handleFixedCell(List fixedCells, int index) { removeDeportedCells(); if (fixedCells.isEmpty()) { return; @@ -490,6 +487,7 @@ private void handleFixedCell(List fixedCells, int index, GridRow topRo * we need to put it in another row. */ if (skin.rowToLayout.get(index)) { + GridRow topRow = skin.getFlow().getTopRow(); if (topRow != null) { for (CellView cell : fixedCells) { if (!cell.isEditing()) { From 6cf5723ea271217fb7dd9737b903c305b567415d Mon Sep 17 00:00:00 2001 From: "samir.hadzic.pro@gmail.com" Date: Wed, 9 Oct 2019 11:13:35 +0200 Subject: [PATCH 17/17] Javadoc updated --- .../samples/spreadsheet/BrowserImpl.java | 18 ++++++++++- .../spreadsheet/HelloSpreadsheetView2.java | 5 +-- .../spreadsheet/CellGraphicFactory.java | 19 ++++++------ .../control/spreadsheet/SpreadsheetCell.java | 9 ++++-- .../spreadsheet/SpreadsheetCellBase.java | 31 ++++++++++++------- 5 files changed, 55 insertions(+), 27 deletions(-) diff --git a/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java index 4211634ab..307a0d12a 100644 --- a/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java +++ b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/BrowserImpl.java @@ -29,7 +29,10 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; @@ -58,6 +61,19 @@ public Node getNode(SpreadsheetCell cell) { if (browserList.isEmpty()) { browserCounter.incrementAndGet(); WebView webView = new WebView(); + webView.setMouseTransparent(true); + webView.setContextMenuEnabled(false); + webView.setPrefHeight(48); + //https://stackoverflow.com/questions/11206942/how-to-hide-scrollbars-in-the-javafx-webview?lq=1 + webView.getChildrenUnmodifiable().addListener(new InvalidationListener() { + @Override + public void invalidated(Observable observable) { + Set deadSeaScrolls = webView.lookupAll(".scroll-bar"); + for (Node scroll : deadSeaScrolls) { + scroll.setVisible(false); + } + } + }); webView.getEngine().loadContent( cell.getItem().toString()); loadedUrl.put(webView, cell.getItem().toString()); return webView; @@ -93,4 +109,4 @@ public void setUnusedNode(WebView browser) { public Class getType() { return WebView.class; } -} + } diff --git a/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/HelloSpreadsheetView2.java b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/HelloSpreadsheetView2.java index 27b60987a..86b899336 100644 --- a/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/HelloSpreadsheetView2.java +++ b/controlsfx-samples/src/main/java/org/controlsfx/samples/spreadsheet/HelloSpreadsheetView2.java @@ -155,6 +155,7 @@ public void changed(ObservableValue arg0, Boolean arg1, Boole grid.add(columnHeaderLabel, 0, row); columnHeader.setSelected(true); spreadSheetView.setShowColumnHeader(true); + spreadSheetView.setCellGraphicFactory(new BrowserImpl()); grid.add(columnHeader, 1, row++); columnHeader.selectedProperty().addListener(new ChangeListener() { @@ -326,9 +327,9 @@ private ObservableList getTitle(GridBase grid, int row) { final ObservableList title = FXCollections.observableArrayList(); - SpreadsheetCell cell = SpreadsheetCellType.STRING.createCell(row, 0, 1, 1, "Customer order details"); + SpreadsheetCell cell = SpreadsheetCellType.STRING.createCell(row, 0, 1, 1, "Customer order details"); cell.setEditable(false); - cell.getStyleClass().add("title"); + cell.setCellGraphic(true); title.add(cell); for (int column = 1; column < grid.getColumnCount(); ++column) { diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/CellGraphicFactory.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/CellGraphicFactory.java index 2aaba277c..9e482272b 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/CellGraphicFactory.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/CellGraphicFactory.java @@ -35,13 +35,12 @@ /** * - * If anyone wants to display a specific Graphic in a SpreadsheetCell, a - * solution is to provide a Node (for example a {@link WebView}) that will be - * displayed in the cell. + * If anyone wants to display a specific Graphic only for visible + * {@code SpreadsheetCell}, a solution is to provide a Node (for example a + * {@link WebView}) that will be displayed in the cell. * - * Because a Node can consume a lot of memory, we need this - * {@code CellGraphicFactory} that will recycle the Nodes to only provide them - * for visible cells. + * Because a Node can consume a lot of memory, this {@code CellGraphicFactory} + * will recycle the Nodes to only provide them for visible cells. */ public interface CellGraphicFactory { @@ -57,11 +56,11 @@ public interface CellGraphicFactory { /** * When a {@code Node} is reused (transfered from one cell to another for - * example), we ask the Node to reload. Beware, only reload when necessary! - * This method can be called several times with the same {@code Node} and - * itemValue. + * example), we ask the Node to reload. Beware, only reload when necessary : + * This method can be called several times with the same {@code node} and + * {@code cell}. * - * @param node the considered {@code Node} + * @param node the {@code Node} affected to the {@code cell} * @param cell the considered SpreadsheetCell */ public void load(T node, SpreadsheetCell cell); diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java index f662b3e9d..1d0f93beb 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCell.java @@ -141,9 +141,9 @@ public static enum CornerPosition { /** * Returns {@code true} if this cell contains something particular in its - * item and a Node given by the {@link CellGraphicFactory} will be used to + * item and a {@code Node} given by the {@link CellGraphicFactory} will be used to * display it. - * + * * @return {@code true} if this cell item needs to be given to a particular * Node */ @@ -151,9 +151,12 @@ public static enum CornerPosition { /** * If {@code isCellGraphic} is {@code true}, this cell item contains - * something particular and should be display by using + * something particular and should be display by using a Node provided by * {@link CellGraphicFactory} object in the CellView. * + * If you only seek to place a simple {@code Node} for this cell, simply use {@link #setGraphic(javafx.scene.Node) + * }. + * * @param isCellGraphic if {@code true}, a Node will be used to display * something particular for the cell */ diff --git a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java index 23b8d4b80..1ccf91868 100644 --- a/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java +++ b/controlsfx/src/main/java/org/controlsfx/control/spreadsheet/SpreadsheetCellBase.java @@ -126,25 +126,30 @@ * feature is completely different from the {@link Filter}. Filters are shown on * one particular row whereas popup can be added to every cell. * - * + * *

Graphic

* Each cell can have a graphic to display next to the text in the cells. Just * use the {@link #setGraphic(Node)} in order to specify the graphic you want. - * If you specify an {@link ImageView}, the SpreadsheetView will try to resize it in - * order to fit the space available in the cell. - * + * If you specify an {@link ImageView}, the SpreadsheetView will try to resize + * it in order to fit the space available in the cell. + * * For example : - * + * *
  * cell.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("icons/exclamation.png"))));
  * 
- * - *
SpreadsheetCellBase with graphic

+ * + * If you only want to only provide Graphics for visible cells (for example a + * {@code WebView}), you can toggle {@link #setCellGraphic(boolean) } and + * provide the graphics with the factory in {@link SpreadsheetView#setCellGraphicFactory(org.controlsfx.control.spreadsheet.CellGraphicFactory) + * }. + *
SpreadsheetCellBase with graphic
+ *
* In addition to that, you can also specify another graphic property to your - * cell with {@link #activateCorner(org.controlsfx.control.spreadsheet.SpreadsheetCell.CornerPosition) }. - * This allow you to activate or deactivate some graphics on the cell in every - * corner. Right now it's a little red triangle but you can modify this in your CSS by - * using the "cell-corner" style class. + * cell with {@link #activateCorner(org.controlsfx.control.spreadsheet.SpreadsheetCell.CornerPosition) + * }. This allow you to activate or deactivate some graphics on the cell in + * every corner. Right now it's a little red triangle but you can modify this in + * your CSS by using the "cell-corner" style class. * *
  * .cell-corner.top-left{
@@ -350,10 +355,14 @@ public boolean isWrapText(){
         return isSet(WRAP_BIT_POSITION);
     }
     
+    /** {@inheritDoc} */
+    @Override
     public boolean isCellGraphic(){
         return isSet(IS_BROWSER_POSITION);
     }
     
+    /** {@inheritDoc} */
+    @Override
     public void setCellGraphic(boolean isBrowser){
         setMask(isBrowser, IS_BROWSER_POSITION);
     }