@@ -13,7 +13,7 @@ closeComment: > | |||
markComment: | | |||
Hello there! | |||
It looks like this issue hasn't progressed lately. There are so many issues that we just can't deal them all within a reasonable timeframe. | |||
We are sorry that this issue hasn't progressed lately. We are prioritizing issues by severity and the number of customers we expect are experiencing this and haven't gotten around to fix this issue yet. | |||
There are a couple of things you could help to get things rolling on this issue (this is an automated message, so expect that some of these are already in use): | |||
@@ -144,6 +144,10 @@ You should receive comments within a week or so; if that does not happen, make s | |||
# Submitting the patches | |||
## Source code auto-formatting | |||
Before submitting changes to Github, run the command `mvn process-sources` from project's root. This maven goal automatically formats source code according to Vaadin standards. The same goal is run automatically if you invoke `mvn install` or `mvn compile`. | |||
## Creating a pull request in GitHub | |||
All our projects accept contributions as GitHub pull requests. The first time you create a pull request, you will be asked to electronically sign a contribution agreement. |
@@ -2,30 +2,30 @@ | |||
## Project setup | |||
The project currently supports running TestBench 3+ (java) tests. Each test consists of a Vaadin UI class and a TestBench script/java test. | |||
All test UI classes go into uitest/src. These files are automatically packaged into a war file which is deployed to a Jetty server during the build process so that tests can open and interact with the UI:s. For development purposes, the Jetty server can be started in Eclipse, see running tests in Eclipse. | |||
All test UI classes go into `uitest/src`. These files are automatically packaged into a war file which is deployed to a Jetty server during the build process so that tests can open and interact with the UI:s. For development purposes, the Jetty server can be started in Eclipse, see running tests in Eclipse. | |||
The project is setup so that /run is mapped to a specialized servlet which allows you to add the UI you want to test to the URL, e.g. http://localhost:8888/run/com.vaadin.tests.component.label.LabelModes or just http://localhost:8888/run/LabelModes if there are no multiple classes named LabelModes. Because of caching, the ?restartApplication parameter is needed after the first run if you want to run multiple test classes. | |||
The project is setup so that `/run` is mapped to a specialized servlet which allows you to add the UI you want to test to the URL, e.g. http://localhost:8888/run/com.vaadin.tests.component.label.LabelModes or just http://localhost:8888/run/LabelModes if there are no multiple classes named LabelModes. Because of caching, the ?restartApplication parameter is needed after the first run if you want to run multiple test classes. | |||
## Creating a new test | |||
Creating a new test involves two steps: Creating a test UI (typically this should be provided in the ticket) and creating a TestBench test for the UI. | |||
## Creating a new test UI | |||
Test UIs are created inside the uitest/src folder in the com.vaadin.tests package. For tests for a given component, use the com.vaadin.tests.components.<component> package. For other features, use a suitable existing com.vaadin.tests.<something> package or create a new one if no suitable exists. | |||
Test UIs are created inside the `uitest/src` folder in the `com.vaadin.tests` package. For tests for a given component, use the `com.vaadin.tests.components.<component>` package. For other features, use a suitable existing com.vaadin.tests.<something> package or create a new one if no suitable exists. | |||
The test should be named according to what it tests, e.g. EnsureFormTooltipWorks. Names should not refer to a ticket, e.g. Ticket1123 will automatically be rejected during code review as it is non-descriptive. | |||
There are a couple of helper UI super classes which you can use for the test. You should never extend UI directly: | |||
* AbstractTestUI | |||
* `AbstractTestUI` | |||
* Automatically sets up a VerticalLayout with a description label on the top. Use addComponent() to add components to the layout | |||
* Supports ?transport=websocket/streaming/xhr parameter for setting push mode. This is for testing core push functionality, typically you should just add @Push to your test UI | |||
* AbstractTestUIWithLog | |||
* `AbstractTestUIWithLog` | |||
* AbstractTestUI but adds a log at the top to which you can add rows using log(String). Handy for printing state information which the TB test can assert reliably. | |||
* ~~AbstractTestCase, TestBase~~ | |||
* Old base classes for tests. Don’t use for any new tests. Extends LegacyApplication. | |||
* AbstractComponentTest, AbstractFieldTest, ... | |||
* `AbstractComponentTest`, `AbstractFieldTest`, ... | |||
* Base classes for generic component tests. Generates test which have a menu on the top containing options for configuring the component. Classes follow the same component hierarchy as Vaadin component classes and this way automatically gets menu items for setting features the parent class supports. | |||
* Gotcha: If you add a new feature to a menu you need to run and possibly (probably) fix all TB tests which use the class as they will click on the wrong item (fixable by implementing [http://dev.vaadin.com/ticket/11307](http://dev.vaadin.com/ticket/11307)) | |||
[Note] `AbstractTestCase` and `TestBase` are old base classes for tests. Don't use them for any new tests. They extend `LegacyApplication` which is a deprecated class. | |||
## Creating a TestBench test for a UI | |||
All new test for the projects must be created as TestBench3+ tests | |||
### Test class naming | |||
@@ -33,10 +33,10 @@ TestBench 3+ tests usually follow a naming convention which automatically maps t | |||
### Super class | |||
There are a couple of super classes you can use for a TB3+ test: | |||
* MultiBrowserTest | |||
* `MultiBrowserTest` | |||
* Ensures the test is run on the browsers we support automatically | |||
* **This is what you typically should use** | |||
* WebsocketTest | |||
* `WebsocketTest` | |||
* Run only on browsers which supports websockets | |||
### Creating the test | |||
@@ -46,11 +46,11 @@ The actual test is one method in the test class created for the given UI. The me | |||
public void testLabelModes() throws Exception { | |||
“@Test” is needed for it to be run at all. | |||
`@Test` is needed for it to be run at all. | |||
“throws Exception” should usually be added to avoid catching exceptions inside the test, as most often an exception means the test has failed. Unhandled exceptions will always fail the test; if you use checked exceptions, you'll need to handle them somehow or decide they're automatically failures on exception. | |||
`throws Exception` should usually be added to avoid catching exceptions inside the test, as most often an exception means the test has failed. Unhandled exceptions will always fail the test; if you use checked exceptions, you'll need to handle them somehow or decide they're automatically failures on exception. | |||
The beginning of the test should request the UI using openTestURL(); | |||
The beginning of the test should request the UI using `openTestURL()`; | |||
public void testLabelModes() throws Exception { | |||
// Causes the test to be opened with the debug console open. Typically not needed | |||
@@ -97,15 +97,15 @@ Use ids in your UI class. Define the IDs as constants in the UI class. Use the c | |||
## Running the DevelopmentServerLauncher | |||
The Jetty included in the project can be started in Eclipse by running the “Development Server (Vaadin)” launch configuration in /eclipse (right click -> Debug as -> Development Server (Vaadin) ). This deploys all the tests to localhost:8888 (runs on port 8888, don’t change that unless you want problems). Use /run/<uiclass> to open a test. | |||
The Jetty included in the project can be started in Eclipse by running the “Development Server (Vaadin)” launch configuration in `/eclipse` (right click -> Debug as -> Development Server (Vaadin) ). This deploys all the tests to `localhost:8888` (runs on port 8888, don’t change that unless you want problems). Use `/run/<uiclass>` to open a test. | |||
The DevelopmentServerLauncher has a built-in feature which ensures only one instance can be running at a time. There is therefore no need to first stop the old and and then start a new one. Start the server and the old one will be killed automatically. | |||
## Setup before running any tests in Eclipse | |||
Before running any tests in Eclipse you need to | |||
1. copy uitest/eclipse-run-selected-test.properties to work/eclipse-run-selected-test.properties | |||
2. edit work/eclipse-run-selected-test.properties | |||
1. Define com.vaadin.testbench.screenshot.directory as the directory where you checked out the screenshots repository (this directory contains the “references” subdirectory) | |||
1. copy `uitest/eclipse-run-selected-test.properties` to `work/eclipse-run-selected-test.properties` | |||
2. edit `work/eclipse-run-selected-test.properties` | |||
1. Define `com.vaadin.testbench.screenshot.directory` as the directory where you checked out the screenshots repository (this directory contains the “references” subdirectory) | |||
## Running TB3+ tests | |||
@@ -116,15 +116,19 @@ TB3+ tests are standard JUnit tests which can be run using the “Run as -> JUni | |||
#### Debugging remotely | |||
Running remotely on a single browser (as described above) can be used to debug issues with a given browser but with the downside that you cannot see what is happening with the browser. In theory this is possible if you figure out on what machine the test is run (no good way for this at the moment) and use VNC to connect to that. | |||
#### Debugging locally | |||
A better option is to run the test on a local browser instance. To do this you need to add a `@RunLocally` annotation to the test class. @RunLocally uses Firefox by default but also supports other browsers using `@RunLocally(CHROME)`, `@RunLocally(SAFARI)` and `@RunLocally(PHANTOMJS)`. | |||
A better option is to run the test on a local browser instance. To do this you need to add a `@RunLocally` annotation to the test class. `@RunLocally` uses Firefox by default but also supports other browsers using `@RunLocally(CHROME)`, `@RunLocally(SAFARI)`, `@RunLocally(Browser.IE11)` and `@RunLocally(PHANTOMJS)`. | |||
By default using `@RunLocally` annotation in Framework tests is not allowed. In order to run a test locally, you need to uncomment the line `com.vaadin.testbench.allowRunLocally=true` in `work/eclipse-run-selected-test.properties`. | |||
Some local configuration is needed for certain browsers, especially if you did not install them in the “standard” locations. | |||
Besides, some local configurations are needed for certain browsers, especially if you did not install them in the “standard” locations. | |||
**PhantomJS**: PhantomJS is a headless browser, good especially for fast validation. Download it from the [PhantomJS site](http://phantomjs.org/download.html) and add the binary to your PATH. | |||
**Firefox**: If you have Firefox in your PATH, this is everything you need. If Firefox cannot be started, add a **firefox.path** property to `/work/eclipse-run-selected-test.properties`, pointing to your Firefox binary (e.g.`firefox.path=/Applications/Firefox 17 ESR.app/Contents/MacOS/firefox`) | |||
**Firefox**: If you have Firefox in your PATH, this is everything you need. If Firefox cannot be started, add a **firefox.path** property to `/work/eclipse-run-selected-test.properties`, pointing to your Firefox binary (e.g.`firefox.path=/Applications/Firefox 17 ESR.app/Contents/MacOS/firefox`). | |||
**Chrome**: You need to [download ChromeDriver](http://chromedriver.storage.googleapis.com/index.html) and add a chrome.driver.path property to `/work/eclipse-run-selected-test.properties`, pointing to the ChromeDriver binary (NOT the Chrome binary). | |||
**Chrome**: You need to [download ChromeDriver](http://chromedriver.storage.googleapis.com/index.html) and add a chrome.driver.path property to `/work/eclipse-run-selected-test.properties`, pointing to the ChromeDriver binary (NOT the Chrome binary) | |||
**IE11**: You need to [download IEDriverServer according to the selenium version](http://selenium-release.storage.googleapis.com/index.html) and add a webdriver.ie.driver property to `/work/eclipse-run-selected-test.properties`, point to the IEDriveServer binary. | |||
**Safari**: At least on Mac, no configuration should be needed. | |||
@@ -65,7 +65,7 @@ The following preferences need to be set to keep the project consistent. You nee | |||
## Setting up IntelliJ IDEA to Develop Vaadin Framework 8 | |||
1. Intall and run IDEA. Ultimate Edition is better but Community Edition should also work. | |||
1. Install and run IDEA. Ultimate Edition is better but Community Edition should also work. | |||
1. Ensure if Git and Maven plugins are installed, properly configured and enabled. | |||
1. Clone the repository, using menu VCS -> Checkout from Version Control -> Git -> Git Repository URL -> https://github.com/vaadin/framework.git. | |||
When the repository is cloned, do **NOT** open it as a project. |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>vaadin-all</artifactId> | |||
<name>vaadin-all</name> |
@@ -23,7 +23,7 @@ | |||
<body> | |||
<div id="header"> | |||
<h1>Vaadin – thinking of U and I</h1> | |||
<h1>Vaadin – Fight For Simplicity</h1> | |||
<div id="version"> | |||
<strong>Version @version@</strong> | |||
</div> | |||
@@ -65,7 +65,7 @@ | |||
a number of new features and bug fixes, as listed in the <a | |||
href="#enhancements">list of enhancements</a> and <a | |||
href="#changelog">change log</a> below. | |||
The API in this alpha version is not considered final and may change based on your feedback. | |||
The API in this beta version is not considered final and may change based on your feedback. | |||
</p> | |||
<!-- ================================================================ --> | |||
@@ -83,27 +83,26 @@ | |||
enhancements. Below is a list of the most notable changes:</p> | |||
<ul> | |||
<li></li> | |||
</ul> | |||
</p> | |||
<p> | |||
For enhancements introduced in Vaadin Framework 8.4, see the <a | |||
href="http://vaadin.com/download/release/8.4/8.4.0/release-notes.html">Release | |||
Notes for Vaadin Framework 8.4.0</a>. | |||
For enhancements introduced in Vaadin Framework 8.7, see the <a | |||
href="http://vaadin.com/download/release/8.7/8.7.0/release-notes.html">Release | |||
Notes for Vaadin Framework 8.7.0</a>. | |||
For migrating from previous framework versions, see <a href="#incompatible">the list of incompatible changes</a> and <a href="#migrating">how to migrate | |||
to Vaadin Framework 8</a>. | |||
</p> | |||
<h2 id="incompatible">Incompatible or Behavior-altering Changes in @version-minor@</h2> | |||
<li><tt>AbstractSingleSelect</tt> communication has been partially re-written. Some <tt>protected</tt> API was removed.</li> | |||
<li><tt>setStyleName(String, boolean)</tt> has been moved from <tt>AbstractComponent</tt> to <tt>Component</tt> interface as a default method.</li> | |||
<li><tt>Window</tt> closing animation disabled by default.</li> | |||
<li><tt>FileTypeResolver</tt> icon features have been moved to a compatibility version of the class.</li> | |||
<h2>For incompatible or behavior-altering changes in 8.4, please see <a href="https://vaadin.com/download/release/8.4/8.4.0/release-notes.html#incompatible">8.4 release notes</a></h2> | |||
<ul> | |||
<li></li> | |||
</ul> | |||
<h2>For incompatible or behavior-altering changes in 8.7, please see <a href="https://vaadin.com/download/release/8.7/8.7.0/release-notes.html#incompatible">8.7 release notes</a></h2> | |||
<h3 id="knownissues">Known Issues and Limitations</h3> | |||
<ul> | |||
@@ -355,12 +354,12 @@ | |||
</p> | |||
<ul id="supportedservers"> | |||
<li>Apache Tomcat 7-8</li> | |||
<li>Apache Tomcat 7-9</li> | |||
<li>Apache TomEE 1.7 and 7.0</li> | |||
<li>Oracle WebLogic Server 12.2</li> | |||
<li>IBM WebSphere Application Server 9</li> | |||
<li>JBoss EAP 6</li> | |||
<li>Wildfly 8-11</li> | |||
<li>Wildfly 8-13</li> | |||
<li>Jetty 8-9</li> | |||
<li>Glassfish 4</li> | |||
<li>Payara Server 164</li> | |||
@@ -383,11 +382,11 @@ | |||
</p> | |||
<ul> | |||
<li>Mozilla Firefox (latest version, currently 54)</li> | |||
<li>Mozilla Firefox ESR (latest version, currently 52 ESR)</li> | |||
<li>Mozilla Firefox 54+</li> | |||
<li>Mozilla Firefox ESR 52+</li> | |||
<li>Internet Explorer 11, Edge (latest version)</li> | |||
<li>Safari 9+</li> | |||
<li>Google Chrome (latest version, currently 59)</li> | |||
<li>Google Chrome 59+</li> | |||
</ul> | |||
<p> | |||
@@ -438,7 +437,7 @@ | |||
<div id="footer"> | |||
<span class="slogan"><strong>vaadin <em>}></em> | |||
</strong> thinking of U and I<span> <a href="#top">↑ Back | |||
</strong> Fight For Simplicity</span> <a href="#top">↑ Back | |||
to top</a> | |||
</div> | |||
<!-- /footer --> |
@@ -11,16 +11,16 @@ | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-bom</artifactId> | |||
<packaging>pom</packaging> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
<name>Vaadin Framework (Bill of Materials)</name> | |||
<description>Vaadin Framework (Bill of Materials)</description> | |||
<url>http://vaadin.com</url> | |||
<properties> | |||
<vaadin.spring.version>3.0.0</vaadin.spring.version> | |||
<vaadin.testbench.version>5.1.2</vaadin.testbench.version> | |||
<vaadin.cdi.version>3.0.0</vaadin.cdi.version> | |||
<vaadin.context-menu.version>2.0.0</vaadin.context-menu.version> | |||
<vaadin.spring.version>3.1.1</vaadin.spring.version> | |||
<vaadin.testbench.version>5.2.0</vaadin.testbench.version> | |||
<vaadin.cdi.version>3.0.1</vaadin.cdi.version> | |||
<vaadin.context-menu.version>3.0.0</vaadin.context-menu.version> | |||
</properties> | |||
<repositories> |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<!-- Needed by a plugin in release build --> | |||
<groupId>com.vaadin</groupId> |
@@ -15,33 +15,16 @@ | |||
*/ | |||
package com.vaadin.osgi.widgetset; | |||
import org.osgi.service.component.ComponentContext; | |||
import org.osgi.service.component.annotations.Activate; | |||
import org.osgi.service.component.annotations.Component; | |||
import org.osgi.service.component.annotations.Reference; | |||
import org.osgi.service.http.HttpService; | |||
import com.vaadin.osgi.resources.OsgiVaadinResources; | |||
import com.vaadin.osgi.resources.VaadinResourceService; | |||
@Component(immediate = true) | |||
public class DefaultWidgetsetContribution { | |||
private HttpService httpService; | |||
import com.vaadin.osgi.resources.OsgiVaadinWidgetset; | |||
@Component | |||
public class DefaultWidgetsetContribution implements OsgiVaadinWidgetset { | |||
private static final String WIDGETSET_NAME = "com.vaadin.DefaultWidgetSet"; | |||
@Activate | |||
void startup(ComponentContext context) throws Exception { | |||
VaadinResourceService service = OsgiVaadinResources.getService(); | |||
service.publishWidgetset(WIDGETSET_NAME, httpService); | |||
} | |||
@Reference | |||
void setHttpService(HttpService httpService) { | |||
this.httpService = httpService; | |||
} | |||
void unsetHttpService(HttpService httpService) { | |||
this.httpService = null; | |||
@Override | |||
public String getName() { | |||
return WIDGETSET_NAME; | |||
} | |||
} |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>vaadin-client-compiler</artifactId> | |||
<name>vaadin-client-compiler</name> |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<!-- Needed by a plugin in release build --> | |||
<groupId>com.vaadin</groupId> |
@@ -28,7 +28,7 @@ import com.vaadin.shared.AbstractComponentState; | |||
* Updates can be sent back to the server using the | |||
* {@link ApplicationConnection#updateVariable()} methods. | |||
*/ | |||
public interface ComponentConnector extends ServerConnector { | |||
public interface ComponentConnector extends HasWidget { | |||
/* | |||
* (non-Javadoc) | |||
@@ -38,11 +38,6 @@ public interface ComponentConnector extends ServerConnector { | |||
@Override | |||
public AbstractComponentState getState(); | |||
/** | |||
* Returns the widget for this {@link ComponentConnector}. | |||
*/ | |||
public Widget getWidget(); | |||
public LayoutManager getLayoutManager(); | |||
/** |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2018 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.client; | |||
import com.google.gwt.user.client.ui.Widget; | |||
/** | |||
* An interface used by client-side connectors which have widgets. | |||
*/ | |||
public interface HasWidget extends ServerConnector { | |||
/** | |||
* Returns the widget for this {@link ServerConnector}. | |||
*/ | |||
public Widget getWidget(); | |||
} |
@@ -82,6 +82,8 @@ public class Heartbeat { | |||
final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri); | |||
XhrConnection.addXsrfHeaderFromCookie(rb); | |||
final RequestCallback callback = new RequestCallback() { | |||
@Override |
@@ -22,6 +22,7 @@ import com.google.gwt.http.client.RequestBuilder; | |||
import com.google.gwt.http.client.RequestCallback; | |||
import com.google.gwt.http.client.RequestException; | |||
import com.google.gwt.http.client.Response; | |||
import com.google.gwt.user.client.Cookies; | |||
import com.google.gwt.user.client.Timer; | |||
import com.google.gwt.user.client.Window; | |||
import com.vaadin.client.ApplicationConnection; | |||
@@ -49,6 +50,9 @@ import elemental.json.JsonObject; | |||
*/ | |||
public class XhrConnection { | |||
private static final String XSRF_HEADER_NAME = "X-XSRF-TOKEN"; | |||
private static final String XSRF_COOKIE_NAME = "XSRF-TOKEN"; | |||
private ApplicationConnection connection; | |||
/** | |||
@@ -183,6 +187,9 @@ public class XhrConnection { | |||
*/ | |||
public void send(JsonObject payload) { | |||
RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, getUri()); | |||
addXsrfHeaderFromCookie(rb); | |||
// TODO enable timeout | |||
// rb.setTimeoutMillis(timeoutMillis); | |||
// TODO this should be configurable | |||
@@ -244,6 +251,13 @@ public class XhrConnection { | |||
return connection.getMessageHandler(); | |||
} | |||
public static void addXsrfHeaderFromCookie(RequestBuilder rb) { | |||
String xsrfTokenVal = Cookies.getCookie(XSRF_COOKIE_NAME); | |||
if (xsrfTokenVal != null && !xsrfTokenVal.isEmpty()) { | |||
rb.setHeader(XSRF_HEADER_NAME, xsrfTokenVal); | |||
} | |||
} | |||
private static native boolean resendRequest(Request request) | |||
/*-{ | |||
var xhr = request.@com.google.gwt.http.client.Request::xmlHttpRequest |
@@ -15,12 +15,20 @@ | |||
*/ | |||
package com.vaadin.client.connectors.grid; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.logging.Logger; | |||
import com.google.gwt.core.client.GWT; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.google.gwt.user.client.ui.SimplePanel; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.client.ComponentConnector; | |||
import com.vaadin.client.ConnectorMap; | |||
import com.vaadin.client.renderers.Renderer; | |||
import com.vaadin.client.renderers.WidgetRenderer; | |||
import com.vaadin.client.ui.AbstractComponentConnector; | |||
import com.vaadin.client.ui.AbstractConnector; | |||
import com.vaadin.client.widget.grid.RendererCellReference; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.grid.renderers.ComponentRendererState; | |||
@@ -37,6 +45,9 @@ import com.vaadin.ui.renderers.ComponentRenderer; | |||
public class ComponentRendererConnector | |||
extends AbstractGridRendererConnector<String> { | |||
private HashSet<String> knownConnectors = new HashSet<>(); | |||
private HandlerRegistration handlerRegistration; | |||
@Override | |||
protected Renderer<String> createRenderer() { | |||
return new WidgetRenderer<String, SimplePanel>() { | |||
@@ -51,12 +62,21 @@ public class ComponentRendererConnector | |||
@Override | |||
public void render(RendererCellReference cell, String connectorId, | |||
SimplePanel widget) { | |||
createConnectorHierarchyChangeHandler(); | |||
Widget connectorWidget = null; | |||
if (connectorId != null) { | |||
ComponentConnector connector = (ComponentConnector) ConnectorMap | |||
.get(getConnection()).getConnector(connectorId); | |||
widget.setWidget(connector.getWidget()); | |||
if (connector != null) { | |||
connectorWidget = connector.getWidget(); | |||
knownConnectors.add(connectorId); | |||
} | |||
} | |||
if (connectorWidget != null) { | |||
widget.setWidget(connectorWidget); | |||
} else if (widget.getWidget() != null) { | |||
widget.remove(widget.getWidget()); | |||
knownConnectors.remove(connectorId); | |||
} | |||
} | |||
}; | |||
@@ -66,4 +86,43 @@ public class ComponentRendererConnector | |||
public ComponentRendererState getState() { | |||
return (ComponentRendererState) super.getState(); | |||
} | |||
@Override | |||
public void onUnregister() { | |||
unregisterHierarchyHandler(); | |||
super.onUnregister(); | |||
} | |||
/** | |||
* Adds a listener for grid hierarchy changes to find detached connectors | |||
* previously handled by this renderer in order to detach from DOM their | |||
* widgets before {@link AbstractComponentConnector#onUnregister()} is | |||
* invoked otherwise an error message is logged. | |||
*/ | |||
private void createConnectorHierarchyChangeHandler() { | |||
if (handlerRegistration == null) { | |||
handlerRegistration = getGridConnector() | |||
.addConnectorHierarchyChangeHandler(event -> { | |||
Iterator<String> iterator = knownConnectors.iterator(); | |||
while (iterator.hasNext()) { | |||
ComponentConnector connector = (ComponentConnector) ConnectorMap | |||
.get(getConnection()) | |||
.getConnector(iterator.next()); | |||
if (connector != null | |||
&& connector.getParent() == null) { | |||
connector.getWidget().removeFromParent(); | |||
iterator.remove(); | |||
} | |||
} | |||
}); | |||
} | |||
} | |||
private void unregisterHierarchyHandler() { | |||
if (this.handlerRegistration != null) { | |||
this.handlerRegistration.removeHandler(); | |||
this.handlerRegistration = null; | |||
} | |||
} | |||
} |
@@ -72,8 +72,11 @@ public class EditorConnector extends AbstractExtensionConnector { | |||
@Override | |||
public void cancel() { | |||
serverInitiated = true; | |||
getParent().getWidget().cancelEditor(); | |||
// Canceling an editor that is not open is a no-op. | |||
if (getParent().getWidget().isEditorActive()) { | |||
serverInitiated = true; | |||
getParent().getWidget().cancelEditor(); | |||
} | |||
} | |||
@Override | |||
@@ -194,7 +197,17 @@ public class EditorConnector extends AbstractExtensionConnector { | |||
@OnStateChange("enabled") | |||
void updateEnabled() { | |||
getParent().getWidget().getEditor().setEnabled(getState().enabled); | |||
boolean enabled = getState().enabled; | |||
Scheduler.ScheduledCommand setEnabledCommand = () -> { | |||
getParent().getWidget().getEditor().setEnabled(enabled); | |||
}; | |||
if (!enabled) { | |||
Scheduler.get().scheduleFinally(setEnabledCommand); | |||
} else { | |||
setEnabledCommand.execute(); | |||
} | |||
} | |||
@OnStateChange("saveCaption") |
@@ -100,6 +100,25 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector { | |||
super.extend(target); | |||
} | |||
@Override | |||
protected boolean isDropAllowedByCriteriaScript(NativeEvent event) { | |||
final String criteriaScript = getState().criteriaScript; | |||
if (criteriaScript == null) { | |||
return true; | |||
} | |||
return executeScript(event, | |||
getTargetElement(event.getEventTarget().cast()), | |||
getDropLocation(getTargetElement(event.getEventTarget().cast()), | |||
event).name(), | |||
criteriaScript); | |||
} | |||
private native boolean executeScript(NativeEvent event, | |||
Element targetElement, String dropLocation, String script) | |||
/*-{ | |||
return new Function('event', 'targetElement', 'dropLocation', script)(event, targetElement, dropLocation); | |||
}-*/; | |||
/** | |||
* Inspects whether the current drop would happen on the whole grid instead | |||
* of specific row as the drop target. This is based on used drop mode, |
@@ -496,6 +496,9 @@ public abstract class AbstractRemoteDataSource<T> implements DataSource<T> { | |||
Range newUsefulData = partition[1]; | |||
if (!newUsefulData.isEmpty()) { | |||
if (!cached.isEmpty()) | |||
discardStaleCacheEntries(); | |||
// Update the parts that are actually inside | |||
int start = newUsefulData.getStart(); | |||
for (int i = start; i < newUsefulData.getEnd(); i++) { | |||
@@ -515,7 +518,6 @@ public abstract class AbstractRemoteDataSource<T> implements DataSource<T> { | |||
if (cached.isEmpty()) { | |||
cached = newUsefulData; | |||
} else { | |||
discardStaleCacheEntries(); | |||
/* | |||
* everything might've become stale so we need to re-check for |
@@ -18,7 +18,7 @@ package com.vaadin.client.extensions; | |||
import com.google.gwt.event.dom.client.ClickEvent; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.google.web.bindery.event.shared.HandlerRegistration; | |||
import com.vaadin.client.ComponentConnector; | |||
import com.vaadin.client.HasWidget; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.shared.extension.PartInformationState; | |||
@@ -50,7 +50,7 @@ public abstract class AbstractEventTriggerExtensionConnector | |||
@Override | |||
protected void extend(ServerConnector target) { | |||
Widget targetWidget = ((ComponentConnector) target).getWidget(); | |||
Widget targetWidget = ((HasWidget) target).getWidget(); | |||
if (targetWidget instanceof EventTrigger) { | |||
String partInformation = getState().partInformation; | |||
eventHandlerRegistration = ((EventTrigger) targetWidget) |
@@ -326,13 +326,8 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { | |||
// Currently Safari, Edge and IE don't follow the spec by allowing drop | |||
// if those don't match | |||
// Allow by default when criteria not set | |||
boolean allowed = true; | |||
// Execute criteria script | |||
if (getState().criteriaScript != null) { | |||
allowed = executeScript(event, getState().criteriaScript); | |||
} | |||
boolean allowed = isDropAllowedByCriteriaScript(event); | |||
// Execute criterion defined via API | |||
if (allowed && getState().criteria != null | |||
@@ -365,6 +360,25 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { | |||
return allowed; | |||
} | |||
/** | |||
* Checks if a criteria script exists and, if yes, executes it. This method | |||
* is protected, so subclasses as e.g. GridDropTargetConnector can override | |||
* it to add additional script parameters. | |||
* | |||
* @param event | |||
* browser event (dragEnter, dragOver, drop) that should be | |||
* evaluated by the criteria script | |||
* @return {@code true} if no script was given or if the script returned | |||
* true, {@code false} otherwise. | |||
*/ | |||
protected boolean isDropAllowedByCriteriaScript(NativeEvent event) { | |||
final String criteriaScript = getState().criteriaScript; | |||
if (criteriaScript == null) { | |||
return true; | |||
} | |||
return executeScript(event, criteriaScript); | |||
} | |||
/** | |||
* Tells if the given array of types contains files. | |||
* <p> |
@@ -938,6 +938,15 @@ public abstract class VAbstractCalendarPanel<R extends Enum<R>> | |||
renderCalendar(true); | |||
} | |||
/** | |||
* Returns the value of initialRenderDone | |||
* | |||
* @since 8.7 | |||
*/ | |||
public boolean isInitialRenderDone() { | |||
return initialRenderDone; | |||
} | |||
/** | |||
* For internal use only. May be removed or replaced in the future. | |||
* |
@@ -16,7 +16,9 @@ | |||
package com.vaadin.client.ui; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.Style.Visibility; | |||
import com.google.gwt.user.client.DOM; | |||
import com.google.gwt.user.client.Event; | |||
import com.vaadin.client.ApplicationConnection; | |||
@@ -81,20 +83,34 @@ public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox | |||
* Gives access to the input element. | |||
* | |||
* @return Element of the CheckBox itself | |||
* @since 8.7 | |||
*/ | |||
private Element getCheckBoxElement() { | |||
public Element getInputElement() { | |||
// public to allow CheckBoxState to access it. | |||
// FIXME: Would love to use a better way to access the checkbox element | |||
return getElement().getFirstChildElement(); | |||
} | |||
/** | |||
* Gives access to the label element. | |||
* | |||
* @return Element of the Label itself | |||
* @since 8.7 | |||
*/ | |||
public Element getLabelElement() { | |||
// public to allow CheckBoxState to access it. | |||
// FIXME: Would love to use a better way to access the label element | |||
return getInputElement().getNextSiblingElement(); | |||
} | |||
@Override | |||
public void setAriaRequired(boolean required) { | |||
AriaHelper.handleInputRequired(getCheckBoxElement(), required); | |||
AriaHelper.handleInputRequired(getInputElement(), required); | |||
} | |||
@Override | |||
public void setAriaInvalid(boolean invalid) { | |||
AriaHelper.handleInputInvalid(getCheckBoxElement(), invalid); | |||
AriaHelper.handleInputInvalid(getInputElement(), invalid); | |||
} | |||
@Override | |||
@@ -116,4 +132,21 @@ public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox | |||
errorIndicatorElement = null; | |||
} | |||
} | |||
@Override | |||
protected void onAttach() { | |||
super.onAttach(); | |||
if (BrowserInfo.get().isSafari()) { | |||
/* | |||
* Sometimes Safari does not render checkbox correctly when | |||
* attaching. Setting the visibility to hidden and a bit later | |||
* restoring will make everything just fine. | |||
*/ | |||
getElement().getStyle().setVisibility(Visibility.HIDDEN); | |||
Scheduler.get().scheduleFinally(() -> { | |||
getElement().getStyle().setVisibility(Visibility.VISIBLE); | |||
}); | |||
} | |||
} | |||
} |
@@ -137,6 +137,7 @@ public class VCheckBoxGroup extends FocusableFlowPanelComposite | |||
widget.setValue( | |||
item.getBoolean(ListingJsonConstants.JSONKEY_ITEM_SELECTED)); | |||
setOptionEnabled(widget, item); | |||
setOptionReadOnly(widget, item); | |||
widget.setStyleName(CLASSNAME_OPTION_SELECTED, widget.getValue()); | |||
if (requireInitialization) { | |||
@@ -197,6 +198,18 @@ public class VCheckBoxGroup extends FocusableFlowPanelComposite | |||
!isEnabled() || !optionEnabled); | |||
} | |||
protected void setOptionReadOnly(VCheckBox checkBox, JsonObject item) { | |||
if (isReadonly()) { | |||
checkBox.addStyleName("v-readonly"); | |||
checkBox.setEnabled(false); | |||
} else { | |||
checkBox.removeStyleName("v-readonly"); | |||
boolean optionEnabled = !item | |||
.getBoolean(ListingJsonConstants.JSONKEY_ITEM_DISABLED); | |||
checkBox.setEnabled(isEnabled() && optionEnabled); | |||
} | |||
} | |||
public boolean isHtmlContentAllowed() { | |||
return htmlContentAllowed; | |||
} | |||
@@ -217,7 +230,7 @@ public class VCheckBoxGroup extends FocusableFlowPanelComposite | |||
public void setReadonly(boolean readonly) { | |||
if (this.readonly != readonly) { | |||
this.readonly = readonly; | |||
optionsToItems.forEach(this::setOptionEnabled); | |||
optionsToItems.forEach(this::setOptionReadOnly); | |||
} | |||
} | |||
@@ -2361,9 +2361,13 @@ public class VComboBox extends Composite implements Field, KeyDownHandler, | |||
case KeyCodes.KEY_ALT: | |||
case KeyCodes.KEY_DOWN: | |||
case KeyCodes.KEY_UP: | |||
case KeyCodes.KEY_RIGHT: | |||
case KeyCodes.KEY_LEFT: | |||
case KeyCodes.KEY_PAGEDOWN: | |||
case KeyCodes.KEY_PAGEUP: | |||
case KeyCodes.KEY_ESCAPE: | |||
case KeyCodes.KEY_HOME: | |||
case KeyCodes.KEY_END: | |||
// NOP | |||
break; | |||
default: |
@@ -119,6 +119,9 @@ public class VDateTimeCalendarPanel | |||
sec.addChangeHandler(this); | |||
} | |||
// Update times | |||
updateTimes(); | |||
final String delimiter = getDateTimeService().getClockDelimeter(); | |||
if (isReadonly()) { | |||
int h = 0; | |||
@@ -171,9 +174,6 @@ public class VDateTimeCalendarPanel | |||
return; | |||
} | |||
// Update times | |||
updateTimes(); | |||
ListBox lastDropDown = getLastDropDown(); | |||
lastDropDown.addKeyDownHandler(event -> { | |||
boolean shiftKey = event.getNativeEvent().getShiftKey(); |
@@ -121,6 +121,8 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public boolean htmlContentAllowed; | |||
public boolean mouseDownPressed; | |||
private Map<String, List<Command>> triggers = new HashMap<>(); | |||
public VMenuBar() { | |||
@@ -225,13 +227,35 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
* For internal use only. May be removed or replaced in the future. | |||
*/ | |||
public String buildItemHTML(UIDL item) { | |||
return buildItemHTML(item.hasAttribute("separator"), | |||
item.getChildCount() > 0, item.getStringAttribute("icon"), | |||
item.getStringAttribute("text")); | |||
} | |||
/** | |||
* Build the HTML content for a menu item. | |||
* <p> | |||
* For internal use only. May be removed or replaced in the future. | |||
* | |||
* @param separator | |||
* the menu item is separator | |||
* @param subMenu | |||
* the menu item contains submenu | |||
* @param iconUrl | |||
* the menu item icon URL or {@code null} | |||
* @param text | |||
* the menu item text. May not be {@code null} | |||
*/ | |||
public String buildItemHTML(boolean separator, boolean subMenu, | |||
String iconUrl, String text) { | |||
// Construct html from the text and the optional icon | |||
StringBuilder itemHTML = new StringBuilder(); | |||
if (item.hasAttribute("separator")) { | |||
if (separator) { | |||
itemHTML.append("<span>---</span>"); | |||
} else { | |||
// Add submenu indicator | |||
if (item.getChildCount() > 0) { | |||
if (subMenu) { | |||
String bgStyle = ""; | |||
itemHTML.append("<span class=\"" + getStylePrimaryName() | |||
+ "-submenu-indicator\"" + bgStyle | |||
@@ -240,11 +264,11 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
itemHTML.append("<span class=\"" + getStylePrimaryName() | |||
+ "-menuitem-caption\">"); | |||
Icon icon = client.getIcon(item.getStringAttribute("icon")); | |||
Icon icon = client.getIcon(iconUrl); | |||
if (icon != null) { | |||
itemHTML.append(icon.getElement().getString()); | |||
} | |||
String itemText = item.getStringAttribute("text"); | |||
String itemText = text; | |||
if (!htmlContentAllowed) { | |||
itemText = WidgetUtil.escapeHTML(itemText); | |||
} | |||
@@ -355,7 +379,6 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
@Override | |||
public void onBrowserEvent(Event e) { | |||
super.onBrowserEvent(e); | |||
// Handle onload events (icon loaded, size changes) | |||
if (DOM.eventGetType(e) == Event.ONLOAD) { | |||
VMenuBar parent = getParentMenu(); | |||
@@ -379,20 +402,26 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
targetItem = item; | |||
} | |||
} | |||
if (targetItem != null) { | |||
switch (DOM.eventGetType(e)) { | |||
case Event.ONMOUSEDOWN: | |||
if (e.getButton() == Event.BUTTON_LEFT) { | |||
if (isEnabled() && targetItem.isEnabled()) { | |||
// Button is clicked, but not yet released | |||
mouseDownPressed = true; | |||
} | |||
} | |||
break; | |||
case Event.ONCLICK: | |||
if (isEnabled() && targetItem.isEnabled()) { | |||
mouseDownPressed = false; | |||
itemClick(targetItem); | |||
} | |||
break; | |||
case Event.ONMOUSEOVER: | |||
LazyCloser.cancelClosing(); | |||
if (isEnabled() && targetItem.isEnabled()) { | |||
itemOver(targetItem); | |||
} | |||
@@ -701,9 +730,13 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
*/ | |||
@Override | |||
public void onClose(CloseEvent<PopupPanel> event) { | |||
hideChildren(); | |||
close(event, true); | |||
} | |||
protected void close(CloseEvent<PopupPanel> event, boolean animated) { | |||
hideChildren(animated, animated); | |||
if (event.isAutoClosed()) { | |||
hideParents(true); | |||
hideParents(true, animated); | |||
menuVisible = false; | |||
} | |||
visibleChildMenu = null; | |||
@@ -739,14 +772,18 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
* Recursively hide all parent menus. | |||
*/ | |||
public void hideParents(boolean autoClosed) { | |||
hideParents(autoClosed, true); | |||
} | |||
public void hideParents(boolean autoClosed, boolean animated) { | |||
if (visibleChildMenu != null) { | |||
popup.hide(); | |||
popup.hide(false, animated, animated); | |||
setSelected(null); | |||
menuVisible = false; | |||
} | |||
if (getParentMenu() != null) { | |||
getParentMenu().hideParents(autoClosed); | |||
getParentMenu().hideParents(autoClosed, animated); | |||
} | |||
} | |||
@@ -873,6 +910,7 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
super.onLoad(); | |||
if (getParentMenu() != null | |||
&& getParentMenu().getParentMenu() == null | |||
&& getParentMenu().getItems().size() >= 1 | |||
&& getParentMenu().getItems().get(0).equals(this)) { | |||
getElement().setAttribute("tabindex", "0"); | |||
} else { | |||
@@ -949,7 +987,7 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
updateStyleNames(); | |||
} | |||
protected void updateStyleNames() { | |||
public void updateStyleNames() { | |||
if (parentMenu == null) { | |||
// Style names depend on the parent menu's primary style name so | |||
// don't do updates until the item has a parent | |||
@@ -1088,7 +1126,7 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
return enabled; | |||
} | |||
private void setSeparator(boolean separator) { | |||
public void setSeparator(boolean separator) { | |||
isSeparator = separator; | |||
updateStyleNames(); | |||
if (!separator) { | |||
@@ -1185,6 +1223,14 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
this.id = id; | |||
} | |||
public void setDescription(String description) { | |||
this.description = description; | |||
} | |||
public void setDescriptionContentMode( | |||
ContentMode descriptionContentMode) { | |||
this.descriptionContentMode = descriptionContentMode; | |||
} | |||
} | |||
/** | |||
@@ -1906,7 +1952,7 @@ public class VMenuBar extends FocusableFlowPanel implements | |||
LazyCloser.schedule(); | |||
} | |||
private VMenuBar getRoot() { | |||
protected VMenuBar getRoot() { | |||
VMenuBar root = this; | |||
while (root.getParentMenu() != null) { |
@@ -24,11 +24,8 @@ import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.ui.Button; | |||
import com.vaadin.client.ApplicationConnection; | |||
import com.vaadin.client.BrowserInfo; | |||
import com.vaadin.client.MouseEventDetailsBuilder; | |||
import com.vaadin.client.StyleConstants; | |||
import com.vaadin.client.Util; | |||
import com.vaadin.client.WidgetUtil.ErrorUtil; | |||
import com.vaadin.shared.MouseEventDetails; | |||
import com.vaadin.shared.ui.button.ButtonServerRpc; | |||
public class VNativeButton extends Button | |||
@@ -59,13 +56,10 @@ public class VNativeButton extends Button | |||
* mouse while clicking it. In this case mouse leaves the button without | |||
* moving. | |||
*/ | |||
private boolean clickPending; | |||
public boolean clickPending; | |||
private boolean cancelNextClick = false; | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public boolean disableOnClick = false; | |||
public VNativeButton() { | |||
setStyleName(CLASSNAME); | |||
@@ -145,20 +139,6 @@ public class VNativeButton extends Button | |||
// (#11854) | |||
setFocus(true); | |||
} | |||
if (disableOnClick) { | |||
setEnabled(false); | |||
// FIXME: This should be moved to NativeButtonConnector along with | |||
// buttonRpcProxy | |||
addStyleName(StyleConstants.DISABLED); | |||
buttonRpcProxy.disableOnClick(); | |||
} | |||
// Add mouse details | |||
MouseEventDetails details = MouseEventDetailsBuilder | |||
.buildMouseEventDetails(event.getNativeEvent(), getElement()); | |||
buttonRpcProxy.click(details); | |||
clickPending = false; | |||
} | |||
@Override |
@@ -29,6 +29,7 @@ import com.vaadin.shared.ui.nativeselect.NativeSelectState; | |||
public class VNativeSelect extends FocusableFlowPanelComposite { | |||
private final ListBox listBox = new ListBox(); | |||
private boolean emptySelectionAllowed = true; | |||
/** | |||
* Creates a new {@code VNativeSelect} instance. | |||
@@ -55,7 +56,11 @@ public class VNativeSelect extends FocusableFlowPanelComposite { | |||
*/ | |||
public void setSelectedItem(String value) { | |||
if (value == null) { | |||
getListBox().setSelectedIndex(-1); | |||
if (emptySelectionAllowed) { | |||
getListBox().setSelectedIndex(0); | |||
} else { | |||
getListBox().setSelectedIndex(-1); | |||
} | |||
} else { | |||
for (int i = 0; i < getListBox().getItemCount(); i++) { | |||
if (Objects.equals(value, getListBox().getValue(i))) { | |||
@@ -132,4 +137,23 @@ public class VNativeSelect extends FocusableFlowPanelComposite { | |||
return getListBox().getVisibleItemCount(); | |||
} | |||
/** | |||
* Returns true if empty selection is allowed. | |||
* | |||
* @since 8.7 | |||
* @return empty selection is allowed | |||
*/ | |||
public boolean isEmptySelectionAllowed() { | |||
return emptySelectionAllowed; | |||
} | |||
/** | |||
* Sets true if empty selection is allowed. | |||
* | |||
* @since 8.7 | |||
* @param emptySelectionAllowed | |||
*/ | |||
public void setEmptySelectionAllowed(boolean emptySelectionAllowed) { | |||
this.emptySelectionAllowed = emptySelectionAllowed; | |||
} | |||
} |
@@ -65,15 +65,6 @@ public class VOverlay extends Overlay { | |||
super(autoHide, modal); | |||
} | |||
/* | |||
* A "thread local" of sorts, set temporarily so that VOverlayImpl knows | |||
* which VOverlay is using it, so that it can be attached to the correct | |||
* overlay container. | |||
* | |||
* TODO this is a strange pattern that we should get rid of when possible. | |||
*/ | |||
protected static VOverlay current; | |||
/** | |||
* Get the {@link ApplicationConnection} that this overlay belongs to. If | |||
* it's not set, {@link #getOwner()} is used to figure it out. | |||
@@ -162,4 +153,15 @@ public class VOverlay extends Overlay { | |||
overlayContainerLabel); | |||
} | |||
/** | |||
* Sets the {@link ApplicationConnection} that this overlay belongs to. | |||
* | |||
* @see #getApplicationConnection() | |||
* | |||
* @param ac | |||
* the connection | |||
*/ | |||
public void setApplicationConnection(ApplicationConnection ac) { | |||
this.ac = ac; | |||
} | |||
} |
@@ -16,8 +16,10 @@ | |||
package com.vaadin.client.ui; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.google.gwt.aria.client.Id; | |||
import com.google.gwt.aria.client.LiveValue; | |||
@@ -272,6 +274,9 @@ public class VTabsheet extends VTabsheetBase | |||
public void recalculateCaptionWidth() { | |||
tabCaption.setWidth(tabCaption.getRequiredWidth() + "px"); | |||
if (isVisible()) { | |||
tabBar.tabWidths.put(this, getOffsetWidth()); | |||
} | |||
} | |||
@Override | |||
@@ -443,6 +448,9 @@ public class VTabsheet extends VTabsheetBase | |||
private VTabsheet tabsheet; | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
private Map<Tab, Integer> tabWidths = new HashMap<Tab, Integer>(); | |||
TabBar(VTabsheet tabsheet) { | |||
this.tabsheet = tabsheet; | |||
@@ -502,6 +510,7 @@ public class VTabsheet extends VTabsheetBase | |||
getTabsheet().selectionHandler.registerTab(t); | |||
t.setCloseHandler(this); | |||
tabWidths.put(t, t.getOffsetWidth()); | |||
return t; | |||
} | |||
@@ -522,6 +531,18 @@ public class VTabsheet extends VTabsheetBase | |||
return (Tab) super.getWidget(index); | |||
} | |||
private int getTabIndex(Tab tab) { | |||
if (tab == null) { | |||
return -1; | |||
} | |||
for (int i = 0; i < getTabCount(); i++) { | |||
if (tab.equals(getTab(i))) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
private int getTabIndex(String tabId) { | |||
if (tabId == null) { | |||
return -1; | |||
@@ -593,6 +614,7 @@ public class VTabsheet extends VTabsheetBase | |||
} | |||
remove(tab); | |||
tabWidths.remove(tab); | |||
/* | |||
* If this widget was selected we need to unmark it as the last | |||
@@ -616,6 +638,13 @@ public class VTabsheet extends VTabsheetBase | |||
} | |||
} | |||
private int getLastKnownTabWidth(Tab tab) { | |||
if (tabWidths.containsKey(tab)) { | |||
return tabWidths.get(tab); | |||
} | |||
return 0; | |||
} | |||
private int selectNewShownTab(int oldPosition) { | |||
// After removing a tab, find a new scroll position. In most | |||
// cases the scroll position does not change, but if the tab | |||
@@ -630,7 +659,7 @@ public class VTabsheet extends VTabsheetBase | |||
for (int i = oldPosition - 1; i >= 0; i--) { | |||
Tab tab = getTab(i); | |||
if (!tab.isHiddenOnServer()) { | |||
if (tab != null && !tab.isHiddenOnServer()) { | |||
return i; | |||
} | |||
} | |||
@@ -685,6 +714,13 @@ public class VTabsheet extends VTabsheetBase | |||
} | |||
} | |||
/** | |||
* Returns the index of the last visible tab on the server | |||
*/ | |||
private int getLastVisibleTab() { | |||
return getPreviousVisibleTab(getTabCount()); | |||
} | |||
/** | |||
* Find the previous visible tab. Returns -1 if none is found. | |||
* | |||
@@ -702,7 +738,7 @@ public class VTabsheet extends VTabsheetBase | |||
public int scrollLeft(int currentFirstVisible) { | |||
int prevVisible = getPreviousVisibleTab(currentFirstVisible); | |||
if (prevVisible == -1) { | |||
if (prevVisible < 0) { | |||
return -1; | |||
} | |||
@@ -715,7 +751,7 @@ public class VTabsheet extends VTabsheetBase | |||
public int scrollRight(int currentFirstVisible) { | |||
int nextVisible = getNextVisibleTab(currentFirstVisible); | |||
if (nextVisible == -1) { | |||
if (nextVisible < 0) { | |||
return -1; | |||
} | |||
Tab currentFirst = getTab(currentFirstVisible); | |||
@@ -1335,14 +1371,44 @@ public class VTabsheet extends VTabsheetBase | |||
scrollerIndex = tb.getNextVisibleTab(scrollerIndex); | |||
} | |||
TableCellElement spacerCell = ((TableElement) tb.getElement().cast()) | |||
.getRows().getItem(0).getCells().getItem(tb.getTabCount()); | |||
if (scroller.getStyle().getDisplay() != "none") { | |||
spacerCell.getStyle().setPropertyPx("minWidth", | |||
scroller.getOffsetWidth()); | |||
spacerCell.getStyle().setPropertyPx("minHeight", 1); | |||
} else { | |||
spacerCell.getStyle().setProperty("minWidth", "0"); | |||
spacerCell.getStyle().setProperty("minHeight", "0"); | |||
} | |||
// check if hidden tabs need to be scrolled back into view | |||
int firstVisibleIndex = tb.getFirstVisibleTabClient(); | |||
if (firstVisibleIndex != 0 && getTabCount() > 0 | |||
&& getLeftGap() + getRightGap() > 0) { | |||
int hiddenCount = tb.getTabCount(); | |||
if (firstVisibleIndex > 0) { | |||
hiddenCount -= firstVisibleIndex; | |||
} | |||
int counter = 0; | |||
while ((getLeftGap() + getRightGap() > getFirstOutOfViewWidth()) | |||
&& counter < hiddenCount) { | |||
tb.scrollLeft(tb.getFirstVisibleTabClient()); | |||
scrollerIndex = tb.getFirstVisibleTabClient(); | |||
++counter; | |||
} | |||
} | |||
boolean scrolled = isScrolledTabs(); | |||
boolean clipped = isClippedTabs(); | |||
if (tb.getTabCount() > 0 && tb.isVisible() && (scrolled || clipped)) { | |||
scroller.getStyle().clearDisplay(); | |||
DOM.setElementProperty(scrollerPrev, "className", SCROLLER_CLASSNAME | |||
scrollerPrev.setPropertyString("className", SCROLLER_CLASSNAME | |||
+ (scrolled ? "Prev" : PREV_SCROLLER_DISABLED_CLASSNAME)); | |||
DOM.setElementProperty(scrollerNext, "className", | |||
SCROLLER_CLASSNAME + (clipped ? "Next" : "Next-disabled")); | |||
scrollerNext.setPropertyString("className", | |||
SCROLLER_CLASSNAME + (clipped | |||
&& scrollerIndex != tb.getLastVisibleTab() ? "Next" | |||
: "Next-disabled")); | |||
// the active tab should be focusable if and only if it is visible | |||
boolean isActiveTabVisible = scrollerIndex <= activeTabIndex | |||
@@ -1367,6 +1433,48 @@ public class VTabsheet extends VTabsheetBase | |||
} | |||
} | |||
private int getLeftGap() { | |||
int firstVisibleIndex = tb.getFirstVisibleTabClient(); | |||
int gap; | |||
if (firstVisibleIndex < 0) { | |||
// no tabs are visible, the entire empty space is returned | |||
// through getRightGap() | |||
gap = 0; | |||
} else { | |||
Element tabContainer = tb.getElement().getParentElement(); | |||
Tab firstVisibleTab = tb.getTab(firstVisibleIndex); | |||
gap = firstVisibleTab.getAbsoluteLeft() | |||
- tabContainer.getAbsoluteLeft(); | |||
} | |||
return gap > 0 ? gap : 0; | |||
} | |||
private int getRightGap() { | |||
int lastVisibleIndex = tb.getLastVisibleTab(); | |||
Element tabContainer = tb.getElement().getParentElement(); | |||
int gap; | |||
if (lastVisibleIndex < 0) { | |||
// no tabs visible, return the whole available width | |||
gap = getOffsetWidth() - scroller.getOffsetWidth(); | |||
} else { | |||
Tab lastVisibleTab = tb.getTab(lastVisibleIndex); | |||
gap = tabContainer.getAbsoluteRight() | |||
- lastVisibleTab.getAbsoluteLeft() | |||
- lastVisibleTab.getOffsetWidth() | |||
- scroller.getOffsetWidth() - 2; | |||
} | |||
return gap > 0 ? gap : 0; | |||
} | |||
private int getFirstOutOfViewWidth() { | |||
Tab firstTabOutOfView = tb.getTab( | |||
tb.getPreviousVisibleTab(tb.getFirstVisibleTabClient())); | |||
if (firstTabOutOfView != null) { | |||
return tb.getLastKnownTabWidth(firstTabOutOfView); | |||
} | |||
return 0; | |||
} | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public void showAllTabs() { | |||
scrollerIndex = tb.getFirstVisibleTab(); | |||
@@ -1385,10 +1493,8 @@ public class VTabsheet extends VTabsheetBase | |||
} | |||
private boolean isClippedTabs() { | |||
return (tb.getOffsetWidth() - DOM.getElementPropertyInt( | |||
(Element) tb.getContainerElement().getLastChild().cast(), | |||
"offsetWidth")) > getOffsetWidth() | |||
- (isScrolledTabs() ? scroller.getOffsetWidth() : 0); | |||
return (tb.getOffsetWidth() - getSpacerWidth()) > getOffsetWidth() | |||
- (isScrolledTabs() ? scroller.getOffsetWidth() : 0); | |||
} | |||
private boolean isClipped(Tab tab) { | |||
@@ -1396,6 +1502,12 @@ public class VTabsheet extends VTabsheetBase | |||
+ getOffsetWidth() - scroller.getOffsetWidth(); | |||
} | |||
private int getSpacerWidth() { | |||
int spacerWidth = ((Element) tb.getContainerElement().getLastChild() | |||
.cast()).getPropertyInt("offsetWidth"); | |||
return spacerWidth; | |||
} | |||
@Override | |||
protected void clearPaintables() { | |||
@@ -1584,8 +1696,6 @@ public class VTabsheet extends VTabsheetBase | |||
* | |||
* @param blurSource | |||
* the source. | |||
* @param focusedTabProvider | |||
* provides the current focused tab. | |||
*/ | |||
public BlurCommand(Tab blurSource) { | |||
this.blurSource = blurSource; | |||
@@ -1936,13 +2046,16 @@ public class VTabsheet extends VTabsheetBase | |||
// On IE8 a tab with false visibility would have the bounds of the | |||
// full TabBar. | |||
if (!tab.isVisible()) { | |||
while (!tab.isVisible()) { | |||
while (!tab.isVisible() && scrollerIndex > 0) { | |||
scrollerIndex = tb.scrollLeft(scrollerIndex); | |||
} | |||
updateTabScroller(); | |||
} else if (isClipped(tab)) { | |||
while (isClipped(tab) && scrollerIndex != -1) { | |||
} else if (isClipped(tab) | |||
&& scrollerIndex < tb.getLastVisibleTab()) { | |||
int tabIndex = tb.getTabIndex(tab); | |||
while (isClipped(tab) && scrollerIndex >= 0 | |||
&& scrollerIndex < tabIndex) { | |||
scrollerIndex = tb.scrollRight(scrollerIndex); | |||
} | |||
updateTabScroller(); |
@@ -25,6 +25,7 @@ import com.google.gwt.dom.client.DivElement; | |||
import com.google.gwt.dom.client.Document; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.FormElement; | |||
import com.google.gwt.dom.client.InputElement; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.Timer; | |||
import com.google.gwt.user.client.ui.FileUpload; | |||
@@ -405,4 +406,12 @@ public class VUpload extends SimplePanel { | |||
private static Logger getLogger() { | |||
return Logger.getLogger(VUpload.class.getName()); | |||
} | |||
public void setAcceptMimeTypes(String acceptMimeTypes) { | |||
if (acceptMimeTypes == null || acceptMimeTypes.isEmpty()) { | |||
InputElement.as(fu.getElement()).setAccept(null); | |||
} else { | |||
InputElement.as(fu.getElement()).setAccept(acceptMimeTypes); | |||
} | |||
} | |||
} |
@@ -15,6 +15,10 @@ | |||
*/ | |||
package com.vaadin.client.ui.checkbox; | |||
import java.util.List; | |||
import com.google.gwt.core.client.JsArrayString; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.event.dom.client.ClickEvent; | |||
import com.google.gwt.event.dom.client.ClickHandler; | |||
import com.google.gwt.user.client.DOM; | |||
@@ -30,6 +34,7 @@ import com.vaadin.client.ui.Icon; | |||
import com.vaadin.client.ui.VCheckBox; | |||
import com.vaadin.shared.EventId; | |||
import com.vaadin.shared.MouseEventDetails; | |||
import com.vaadin.shared.ui.ComponentStateUtil; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.checkbox.CheckBoxServerRpc; | |||
import com.vaadin.shared.ui.checkbox.CheckBoxState; | |||
@@ -45,6 +50,22 @@ import com.vaadin.ui.CheckBox; | |||
public class CheckBoxConnector extends AbstractFieldConnector | |||
implements ClickHandler { | |||
/** | |||
* The style names from getState().inputStyles which are currently applied | |||
* to the checkbox. | |||
* | |||
* @since 8.7 | |||
*/ | |||
private JsArrayString inputStyleNames = JsArrayString.createArray().cast(); | |||
/** | |||
* The style names from getState().labelStyles which are currently applied | |||
* to the checkbox. | |||
* | |||
* @since 8.7 | |||
*/ | |||
private JsArrayString labelStyleNames = JsArrayString.createArray().cast(); | |||
@Override | |||
public boolean delegateCaptionHandling() { | |||
return false; | |||
@@ -88,6 +109,12 @@ public class CheckBoxConnector extends AbstractFieldConnector | |||
VCaption.setCaptionText(getWidget(), getState()); | |||
getWidget().setValue(getState().checked); | |||
// Set styles for input and label | |||
updateStyles(getWidget().getInputElement(), inputStyleNames, | |||
getState().inputStyles); | |||
updateStyles(getWidget().getLabelElement(), labelStyleNames, | |||
getState().labelStyles); | |||
} | |||
@Override | |||
@@ -134,4 +161,22 @@ public class CheckBoxConnector extends AbstractFieldConnector | |||
contextEventSunk = true; | |||
} | |||
} | |||
private void updateStyles(Element clientElement, | |||
JsArrayString clientSideStyles, List<String> serverSideStyes) { | |||
// Remove all old stylenames | |||
for (int i = 0; i < clientSideStyles.length(); i++) { | |||
clientElement.removeClassName(clientSideStyles.get(i)); | |||
} | |||
clientSideStyles.setLength(0); | |||
if (ComponentStateUtil.hasStyles(serverSideStyes)) { | |||
// add new style names | |||
for (String newStyle : serverSideStyes) { | |||
clientElement.addClassName(newStyle); | |||
clientSideStyles.push(newStyle); | |||
} | |||
} | |||
} | |||
} |
@@ -117,7 +117,7 @@ public abstract class TextualDateConnector<PANEL extends VAbstractCalendarPanel< | |||
@Override | |||
public void onStateChanged(StateChangeEvent stateChangeEvent) { | |||
String oldLocale = getWidget().getCurrentLocale(); | |||
boolean isReadOnly = getWidget().isReadonly(); | |||
getWidget().parsable = getState().parsable; | |||
super.onStateChanged(stateChangeEvent); | |||
@@ -159,6 +159,10 @@ public abstract class TextualDateConnector<PANEL extends VAbstractCalendarPanel< | |||
} else { | |||
getWidget().calendarToggle.removeStyleName( | |||
VAbstractPopupCalendar.CLASSNAME + "-button-readonly"); | |||
if (getState().readOnly != isReadOnly | |||
&& getWidget().calendar.isInitialRenderDone()) { | |||
getWidget().calendar.renderCalendar(); | |||
} | |||
} | |||
getWidget().setDescriptionForAssistiveDevices( |
@@ -21,6 +21,7 @@ import java.util.Stack; | |||
import com.google.gwt.core.client.GWT; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.user.client.Command; | |||
import com.google.gwt.user.client.Timer; | |||
import com.vaadin.client.ApplicationConnection; | |||
import com.vaadin.client.Paintable; | |||
import com.vaadin.client.TooltipInfo; | |||
@@ -64,110 +65,126 @@ public class MenuBarConnector extends AbstractComponentConnector | |||
// For future connections | |||
widget.client = client; | |||
widget.uidlId = uidl.getId(); | |||
Timer timer = new Timer() { | |||
// Empty the menu every time it receives new information | |||
if (!widget.getItems().isEmpty()) { | |||
widget.clearItems(); | |||
} | |||
UIDL options = uidl.getChildUIDL(0); | |||
if (null != getState() | |||
&& !ComponentStateUtil.isUndefinedWidth(getState())) { | |||
UIDL moreItemUIDL = options.getChildUIDL(0); | |||
StringBuilder itemHTML = new StringBuilder(); | |||
if (moreItemUIDL.hasAttribute("icon")) { | |||
Icon icon = client | |||
.getIcon(moreItemUIDL.getStringAttribute("icon")); | |||
if (icon != null) { | |||
itemHTML.append(icon.getElement().getString()); | |||
@Override | |||
public void run() { | |||
// Empty the menu every time it receives new information | |||
if (!widget.getItems().isEmpty()) { | |||
widget.clearItems(); | |||
} | |||
} | |||
String moreItemText = moreItemUIDL.getStringAttribute("text"); | |||
if ("".equals(moreItemText)) { | |||
moreItemText = "►"; | |||
} | |||
itemHTML.append(moreItemText); | |||
UIDL options = uidl.getChildUIDL(0); | |||
widget.moreItem = GWT.create(VMenuBar.CustomMenuItem.class); | |||
widget.moreItem.setHTML(itemHTML.toString()); | |||
widget.moreItem.setCommand(VMenuBar.emptyCommand); | |||
if (null != getState() | |||
&& !ComponentStateUtil.isUndefinedWidth(getState())) { | |||
UIDL moreItemUIDL = options.getChildUIDL(0); | |||
StringBuilder itemHTML = new StringBuilder(); | |||
widget.collapsedRootItems = new VMenuBar(true, widget); | |||
widget.moreItem.setSubMenu(widget.collapsedRootItems); | |||
widget.moreItem.addStyleName( | |||
widget.getStylePrimaryName() + "-more-menuitem"); | |||
} | |||
UIDL uidlItems = uidl.getChildUIDL(1); | |||
Iterator<Object> itr = uidlItems.iterator(); | |||
Stack<Iterator<Object>> iteratorStack = new Stack<>(); | |||
Stack<VMenuBar> menuStack = new Stack<>(); | |||
VMenuBar currentMenu = widget; | |||
while (itr.hasNext()) { | |||
UIDL item = (UIDL) itr.next(); | |||
VMenuBar.CustomMenuItem currentItem = null; | |||
final int itemId = item.getIntAttribute("id"); | |||
if (moreItemUIDL.hasAttribute("icon")) { | |||
Icon icon = client.getIcon( | |||
moreItemUIDL.getStringAttribute("icon")); | |||
if (icon != null) { | |||
itemHTML.append(icon.getElement().getString()); | |||
} | |||
} | |||
boolean itemHasCommand = item.hasAttribute("command"); | |||
boolean itemIsCheckable = item | |||
.hasAttribute(MenuBarConstants.ATTRIBUTE_CHECKED); | |||
String moreItemText = moreItemUIDL | |||
.getStringAttribute("text"); | |||
if ("".equals(moreItemText)) { | |||
moreItemText = "►"; | |||
} | |||
itemHTML.append(moreItemText); | |||
String itemHTML = widget.buildItemHTML(item); | |||
widget.moreItem = GWT.create(VMenuBar.CustomMenuItem.class); | |||
widget.moreItem.setHTML(itemHTML.toString()); | |||
widget.moreItem.setCommand(VMenuBar.emptyCommand); | |||
Command cmd = null; | |||
if (!item.hasAttribute("separator")) { | |||
if (itemHasCommand || itemIsCheckable) { | |||
// Construct a command that fires onMenuClick(int) with the | |||
// item's id-number | |||
cmd = () -> widget.hostReference.onMenuClick(itemId); | |||
widget.collapsedRootItems = new VMenuBar(true, widget); | |||
widget.moreItem.setSubMenu(widget.collapsedRootItems); | |||
widget.moreItem.addStyleName( | |||
widget.getStylePrimaryName() + "-more-menuitem"); | |||
} | |||
} | |||
currentItem = currentMenu.addItem(itemHTML, cmd); | |||
currentItem.setId("" + itemId); | |||
currentItem.updateFromUIDL(item, client); | |||
if (item.getChildCount() > 0) { | |||
menuStack.push(currentMenu); | |||
iteratorStack.push(itr); | |||
itr = item.iterator(); | |||
currentMenu = new VMenuBar(true, currentMenu); | |||
client.getVTooltip().connectHandlersToWidget(currentMenu); | |||
// this is the top-level style that also propagates to items - | |||
// any item specific styles are set above in | |||
// currentItem.updateFromUIDL(item, client) | |||
if (ComponentStateUtil.hasStyles(getState())) { | |||
for (String style : getState().styles) { | |||
currentMenu.addStyleDependentName(style); | |||
UIDL uidlItems = uidl.getChildUIDL(1); | |||
Iterator<Object> itr = uidlItems.iterator(); | |||
Stack<Iterator<Object>> iteratorStack = new Stack<>(); | |||
Stack<VMenuBar> menuStack = new Stack<>(); | |||
VMenuBar currentMenu = widget; | |||
while (itr.hasNext()) { | |||
UIDL item = (UIDL) itr.next(); | |||
VMenuBar.CustomMenuItem currentItem = null; | |||
final int itemId = item.getIntAttribute("id"); | |||
boolean itemHasCommand = item.hasAttribute("command"); | |||
boolean itemIsCheckable = item | |||
.hasAttribute(MenuBarConstants.ATTRIBUTE_CHECKED); | |||
String itemHTML = widget.buildItemHTML(item); | |||
Command cmd = null; | |||
if (!item.hasAttribute("separator")) { | |||
if (itemHasCommand || itemIsCheckable) { | |||
// Construct a command that fires onMenuClick(int) | |||
// with the | |||
// item's id-number | |||
cmd = () -> widget.hostReference | |||
.onMenuClick(itemId); | |||
} | |||
} | |||
} | |||
currentItem.setSubMenu(currentMenu); | |||
} | |||
while (!itr.hasNext() && !iteratorStack.empty()) { | |||
boolean hasCheckableItem = false; | |||
for (VMenuBar.CustomMenuItem menuItem : currentMenu | |||
.getItems()) { | |||
hasCheckableItem = hasCheckableItem | |||
|| menuItem.isCheckable(); | |||
} | |||
if (hasCheckableItem) { | |||
currentMenu.addStyleDependentName("check-column"); | |||
} else { | |||
currentMenu.removeStyleDependentName("check-column"); | |||
} | |||
currentItem = currentMenu.addItem(itemHTML, cmd); | |||
currentItem.setId("" + itemId); | |||
currentItem.updateFromUIDL(item, client); | |||
if (item.getChildCount() > 0) { | |||
menuStack.push(currentMenu); | |||
iteratorStack.push(itr); | |||
itr = item.iterator(); | |||
currentMenu = new VMenuBar(true, currentMenu); | |||
client.getVTooltip() | |||
.connectHandlersToWidget(currentMenu); | |||
// this is the top-level style that also propagates to | |||
// items - | |||
// any item specific styles are set above in | |||
// currentItem.updateFromUIDL(item, client) | |||
if (ComponentStateUtil.hasStyles(getState())) { | |||
for (String style : getState().styles) { | |||
currentMenu.addStyleDependentName(style); | |||
} | |||
} | |||
currentItem.setSubMenu(currentMenu); | |||
} | |||
itr = iteratorStack.pop(); | |||
currentMenu = menuStack.pop(); | |||
while (!itr.hasNext() && !iteratorStack.empty()) { | |||
boolean hasCheckableItem = false; | |||
for (VMenuBar.CustomMenuItem menuItem : currentMenu | |||
.getItems()) { | |||
hasCheckableItem = hasCheckableItem | |||
|| menuItem.isCheckable(); | |||
} | |||
if (hasCheckableItem) { | |||
currentMenu.addStyleDependentName("check-column"); | |||
} else { | |||
currentMenu | |||
.removeStyleDependentName("check-column"); | |||
} | |||
itr = iteratorStack.pop(); | |||
currentMenu = menuStack.pop(); | |||
} | |||
} | |||
} | |||
}; | |||
getLayoutManager().setNeedsHorizontalLayout(MenuBarConnector.this); | |||
if (widget.mouseDownPressed) { | |||
timer.schedule(getState().delayMs); | |||
widget.mouseDownPressed = false; | |||
} else { | |||
timer.run(); | |||
} | |||
getLayoutManager().setNeedsHorizontalLayout(this); | |||
} | |||
@Override |
@@ -15,19 +15,24 @@ | |||
*/ | |||
package com.vaadin.client.ui.nativebutton; | |||
import com.google.gwt.event.dom.client.ClickEvent; | |||
import com.google.gwt.event.dom.client.ClickHandler; | |||
import com.vaadin.client.MouseEventDetailsBuilder; | |||
import com.vaadin.client.VCaption; | |||
import com.vaadin.client.communication.StateChangeEvent; | |||
import com.vaadin.client.ui.AbstractComponentConnector; | |||
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; | |||
import com.vaadin.client.ui.Icon; | |||
import com.vaadin.client.ui.VNativeButton; | |||
import com.vaadin.shared.MouseEventDetails; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.button.ButtonServerRpc; | |||
import com.vaadin.shared.ui.button.NativeButtonState; | |||
import com.vaadin.ui.NativeButton; | |||
@Connect(NativeButton.class) | |||
public class NativeButtonConnector extends AbstractComponentConnector { | |||
public class NativeButtonConnector extends AbstractComponentConnector | |||
implements ClickHandler { | |||
@Override | |||
public void init() { | |||
@@ -37,6 +42,7 @@ public class NativeButtonConnector extends AbstractComponentConnector { | |||
getWidget().client = getConnection(); | |||
getWidget().paintableId = getConnectorId(); | |||
getWidget().addClickHandler(this); | |||
ConnectorFocusAndBlurHandler.addHandlers(this); | |||
} | |||
@@ -49,8 +55,6 @@ public class NativeButtonConnector extends AbstractComponentConnector { | |||
public void onStateChanged(StateChangeEvent stateChangeEvent) { | |||
super.onStateChanged(stateChangeEvent); | |||
getWidget().disableOnClick = getState().disableOnClick; | |||
// Set text | |||
VCaption.setCaptionText(getWidget(), getState()); | |||
@@ -77,4 +81,21 @@ public class NativeButtonConnector extends AbstractComponentConnector { | |||
public NativeButtonState getState() { | |||
return (NativeButtonState) super.getState(); | |||
} | |||
@Override | |||
public void onClick(ClickEvent event) { | |||
if (getState().disableOnClick) { | |||
getState().enabled = false; | |||
super.updateEnabledState(false); | |||
getRpcProxy(ButtonServerRpc.class).disableOnClick(); | |||
} | |||
// Add mouse details | |||
MouseEventDetails details = MouseEventDetailsBuilder | |||
.buildMouseEventDetails(event.getNativeEvent(), | |||
getWidget().getElement()); | |||
getRpcProxy(ButtonServerRpc.class).click(details); | |||
getWidget().clickPending = false; | |||
} | |||
} |
@@ -95,10 +95,14 @@ public class NativeSelectConnector | |||
ListBox listBox = getWidget().getListBox(); | |||
boolean hasEmptyItem = listBox.getItemCount() > 0 | |||
&& listBox.getValue(0).isEmpty(); | |||
getWidget().setEmptySelectionAllowed(getState().emptySelectionAllowed); | |||
if (hasEmptyItem && getState().emptySelectionAllowed) { | |||
listBox.setItemText(0, getState().emptySelectionCaption); | |||
} else if (hasEmptyItem && !getState().emptySelectionAllowed) { | |||
listBox.removeItem(0); | |||
if (getWidget().getListBox().getSelectedIndex() == 0) { | |||
getWidget().setSelectedItem(null); | |||
} | |||
} else if (!hasEmptyItem && getState().emptySelectionAllowed) { | |||
listBox.insertItem(getState().emptySelectionCaption, 0); | |||
listBox.setValue(0, ""); |
@@ -15,16 +15,6 @@ | |||
*/ | |||
package com.vaadin.client.ui.ui; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.logging.Logger; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.dom.client.Document; | |||
import com.google.gwt.dom.client.Element; | |||
@@ -101,9 +91,18 @@ import com.vaadin.shared.ui.ui.UIServerRpc; | |||
import com.vaadin.shared.ui.ui.UIState; | |||
import com.vaadin.shared.util.SharedUtil; | |||
import com.vaadin.ui.UI; | |||
import elemental.client.Browser; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.logging.Logger; | |||
@Connect(value = UI.class, loadStyle = LoadStyle.EAGER) | |||
public class UIConnector extends AbstractSingleComponentContainerConnector | |||
implements Paintable, MayScrollChildren { | |||
@@ -375,34 +374,18 @@ public class UIConnector extends AbstractSingleComponentContainerConnector | |||
if (uidl.hasAttribute("focused")) { | |||
// set focused component when render phase is finished | |||
Scheduler.get().scheduleDeferred(() -> { | |||
ComponentConnector connector = (ComponentConnector) uidl | |||
.getPaintableAttribute("focused", getConnection()); | |||
Timer timer = new Timer() { | |||
@Override | |||
public void run() { | |||
ComponentConnector connector = (ComponentConnector) uidl | |||
.getPaintableAttribute("focused", | |||
getConnection()); | |||
if (connector == null) { | |||
// Do not try to focus invisible components which not | |||
// present in UIDL | |||
return; | |||
} | |||
focus(connector); | |||
} | |||
}; | |||
final Widget toBeFocused = connector.getWidget(); | |||
/* | |||
* Two types of Widgets can be focused, either implementing GWT | |||
* Focusable of a thinner Vaadin specific Focusable interface. | |||
*/ | |||
if (toBeFocused instanceof com.google.gwt.user.client.ui.Focusable) { | |||
final com.google.gwt.user.client.ui.Focusable toBeFocusedWidget = (com.google.gwt.user.client.ui.Focusable) toBeFocused; | |||
toBeFocusedWidget.setFocus(true); | |||
} else if (toBeFocused instanceof Focusable) { | |||
((Focusable) toBeFocused).focus(); | |||
} else { | |||
getLogger().severe( | |||
"Server is trying to set focus to the widget of connector " | |||
+ Util.getConnectorString(connector) | |||
+ " but it is not focusable. The widget should implement either " | |||
+ com.google.gwt.user.client.ui.Focusable.class | |||
.getName() | |||
+ " or " + Focusable.class.getName()); | |||
} | |||
timer.schedule(0); | |||
}); | |||
} | |||
@@ -437,6 +420,34 @@ public class UIConnector extends AbstractSingleComponentContainerConnector | |||
} | |||
} | |||
private void focus(ComponentConnector connector) { | |||
if (connector == null) { | |||
// Do not try to focus invisible components which not | |||
// present in UIDL | |||
return; | |||
} | |||
final Widget toBeFocused = connector.getWidget(); | |||
/* | |||
* Two types of Widgets can be focused, either implementing GWT | |||
* Focusable of a thinner Vaadin specific Focusable interface. | |||
*/ | |||
if (toBeFocused instanceof com.google.gwt.user.client.ui.Focusable) { | |||
final com.google.gwt.user.client.ui.Focusable toBeFocusedWidget = (com.google.gwt.user.client.ui.Focusable) toBeFocused; | |||
toBeFocusedWidget.setFocus(true); | |||
} else if (toBeFocused instanceof Focusable) { | |||
((Focusable) toBeFocused).focus(); | |||
} else { | |||
getLogger().severe( | |||
"Server is trying to set focus to the widget of connector " | |||
+ Util.getConnectorString(connector) | |||
+ " but it is not focusable. The widget should implement either " | |||
+ com.google.gwt.user.client.ui.Focusable.class | |||
.getName() | |||
+ " or " + Focusable.class.getName()); | |||
} | |||
} | |||
/** | |||
* Reads CSS strings and resources injected by {@link Styles#inject} from | |||
* the UIDL stream. |
@@ -235,6 +235,14 @@ public interface RowContainer { | |||
*/ | |||
public int getRowCount(); | |||
/** | |||
* For internal use only. May be removed or replaced in the future. | |||
* | |||
* @since 8.7 | |||
* @return {@code true} if row height calculations have been scheduled | |||
*/ | |||
public boolean isAutodetectingRowHeightLater(); | |||
/** | |||
* The default height of the rows in this RowContainer. | |||
* |
@@ -644,7 +644,10 @@ public class MultiSelectionRenderer<T> | |||
checkBox.setValue(data, false); | |||
// this should be a temp fix. | |||
checkBox.setText("Selects row number " + getDOMRowIndex(cell) + "."); | |||
checkBox.setEnabled(grid.isEnabled() && !grid.isEditorActive()); | |||
boolean editorOpen = grid.isEditorActive(); | |||
boolean editorBuffered = grid.isEditorBuffered(); | |||
checkBox.setEnabled( | |||
grid.isEnabled() && !(editorOpen && editorBuffered)); | |||
} | |||
private int getDOMRowIndex(RendererCellReference cell) { |
@@ -90,6 +90,8 @@ public class ChildFocusAwareFlowPanel extends FocusableFlowPanel | |||
public ChildFocusAwareFlowPanel() { | |||
eventBus = new HandlerManager(this); | |||
getElement().getStyle().setOutlineStyle(OutlineStyle.NONE); | |||
// The panel itself should not be focused. | |||
getElement().setTabIndex(-1); | |||
super.addFocusHandler(handler); | |||
super.addBlurHandler(handler); | |||
} |
@@ -67,7 +67,6 @@ import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.client.BrowserInfo; | |||
import com.vaadin.client.ComputedStyle; | |||
import com.vaadin.client.DeferredWorker; | |||
import com.vaadin.client.LayoutManager; | |||
import com.vaadin.client.Profiler; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.client.ui.SubPartAware; | |||
@@ -1275,6 +1274,8 @@ public class Escalator extends Widget | |||
private boolean initialColumnSizesCalculated = false; | |||
private boolean autodetectingRowHeightLater = false; | |||
public AbstractRowContainer( | |||
final TableSectionElement rowContainerElement) { | |||
root = rowContainerElement; | |||
@@ -2054,6 +2055,7 @@ public class Escalator extends Widget | |||
defaultRowHeightShouldBeAutodetected = false; | |||
defaultRowHeight = px; | |||
reapplyDefaultRowHeights(); | |||
applyHeightByRows(); | |||
} | |||
@Override | |||
@@ -2115,14 +2117,21 @@ public class Escalator extends Widget | |||
} | |||
public void autodetectRowHeightLater() { | |||
autodetectingRowHeightLater = true; | |||
Scheduler.get().scheduleFinally(() -> { | |||
if (defaultRowHeightShouldBeAutodetected && isAttached()) { | |||
autodetectRowHeightNow(); | |||
defaultRowHeightShouldBeAutodetected = false; | |||
} | |||
autodetectingRowHeightLater = false; | |||
}); | |||
} | |||
@Override | |||
public boolean isAutodetectingRowHeightLater() { | |||
return autodetectingRowHeightLater; | |||
} | |||
private void fireRowHeightChangedEventFinally() { | |||
if (!rowHeightChangedEventFired) { | |||
rowHeightChangedEventFired = true; | |||
@@ -2898,10 +2907,14 @@ public class Escalator extends Widget | |||
*/ | |||
scroller.recalculateScrollbarsForVirtualViewport(); | |||
double spacerHeightsSumUntilIndex = spacerContainer | |||
.getSpacerHeightsSumUntilIndex(index); | |||
final boolean addedRowsAboveCurrentViewport = index | |||
* getDefaultRowHeight() < getScrollTop(); | |||
* getDefaultRowHeight() | |||
+ spacerHeightsSumUntilIndex < getScrollTop(); | |||
final boolean addedRowsBelowCurrentViewport = index | |||
* getDefaultRowHeight() > getScrollTop() | |||
* getDefaultRowHeight() | |||
+ spacerHeightsSumUntilIndex > getScrollTop() | |||
+ getHeightOfSection(); | |||
if (addedRowsAboveCurrentViewport) { | |||
@@ -3895,6 +3908,7 @@ public class Escalator extends Widget | |||
visualRowOrder.getLast()) + 1; | |||
moveAndUpdateEscalatorRows(Range.withOnly(0), | |||
visualRowOrder.size(), newLogicalIndex); | |||
updateTopRowLogicalIndex(1); | |||
} | |||
} | |||
} | |||
@@ -3915,13 +3929,18 @@ public class Escalator extends Widget | |||
Profiler.enter( | |||
"Escalator.BodyRowContainer.reapplyDefaultRowHeights"); | |||
double spacerHeights = 0; | |||
/* step 1: resize and reposition rows */ | |||
for (int i = 0; i < visualRowOrder.size(); i++) { | |||
TableRowElement tr = visualRowOrder.get(i); | |||
reapplyRowHeight(tr, getDefaultRowHeight()); | |||
final int logicalIndex = getTopRowLogicalIndex() + i; | |||
setRowPosition(tr, 0, logicalIndex * getDefaultRowHeight()); | |||
setRowPosition(tr, 0, | |||
logicalIndex * getDefaultRowHeight() + spacerHeights); | |||
spacerHeights += spacerContainer.getSpacerHeight(logicalIndex); | |||
} | |||
/* | |||
@@ -5587,10 +5606,19 @@ public class Escalator extends Widget | |||
*/ | |||
public void shiftSpacersByRows(int index, int numberOfRows) { | |||
final double pxDiff = numberOfRows * body.getDefaultRowHeight(); | |||
for (SpacerContainer.SpacerImpl spacer : getSpacersForRowAndAfter( | |||
index)) { | |||
spacer.setPositionDiff(0, pxDiff); | |||
spacer.setRowIndex(spacer.getRow() + numberOfRows); | |||
List<SpacerContainer.SpacerImpl> spacers = new ArrayList<>( | |||
getSpacersForRowAndAfter(index)); | |||
if (numberOfRows < 0) { | |||
for (SpacerContainer.SpacerImpl spacer : spacers) { | |||
spacer.setPositionDiff(0, pxDiff); | |||
spacer.setRowIndex(spacer.getRow() + numberOfRows); | |||
} | |||
} else { | |||
for (int i = spacers.size() - 1; i >= 0; --i) { | |||
SpacerContainer.SpacerImpl spacer = spacers.get(i); | |||
spacer.setPositionDiff(0, pxDiff); | |||
spacer.setRowIndex(spacer.getRow() + numberOfRows); | |||
} | |||
} | |||
} | |||
@@ -76,10 +76,7 @@ import com.google.gwt.user.client.ui.MenuItem; | |||
import com.google.gwt.user.client.ui.PopupPanel; | |||
import com.google.gwt.user.client.ui.ResizeComposite; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.client.BrowserInfo; | |||
import com.vaadin.client.DeferredWorker; | |||
import com.vaadin.client.Focusable; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.client.*; | |||
import com.vaadin.client.WidgetUtil.Reference; | |||
import com.vaadin.client.data.DataChangeHandler; | |||
import com.vaadin.client.data.DataSource; | |||
@@ -3342,6 +3339,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
*/ | |||
private class AutoColumnWidthsRecalculator { | |||
private double lastCalculatedInnerWidth = -1; | |||
private double lastCalculatedInnerHeight = -1; | |||
private final ScheduledCommand calculateCommand = new ScheduledCommand() { | |||
@@ -3413,6 +3411,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
// Make SelectAllCheckbox visible | |||
getSelectionColumn().ifPresent(col -> { | |||
if (getDefaultHeaderRow() == null) | |||
return; | |||
HeaderCell headerCell = getDefaultHeaderRow().getCell(col); | |||
if (headerCell.getType().equals(GridStaticCellType.WIDGET)) { | |||
// SelectAllCheckbox is present already | |||
@@ -3434,6 +3434,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
// Update latest width to prevent recalculate on height change. | |||
lastCalculatedInnerWidth = escalator.getInnerWidth(); | |||
lastCalculatedInnerHeight = getEscalatorInnerHeight(); | |||
} | |||
private boolean columnsAreGuaranteedToBeWiderThanGrid() { | |||
@@ -9146,6 +9147,17 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
recalculateColumnWidths(); | |||
} | |||
if (getEscalatorInnerHeight() != autoColumnWidthsRecalculator.lastCalculatedInnerHeight) { | |||
Scheduler.get().scheduleFinally(() -> { | |||
// Trigger re-calculation of all row positions. | |||
RowContainer.BodyRowContainer body = getEscalator() | |||
.getBody(); | |||
if (!body.isAutodetectingRowHeightLater()) { | |||
body.setDefaultRowHeight(body.getDefaultRowHeight()); | |||
} | |||
}); | |||
} | |||
// Vertical resizing could make editor positioning invalid so it | |||
// needs to be recalculated on resize | |||
if (isEditorActive()) { | |||
@@ -9159,6 +9171,11 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
}); | |||
} | |||
private double getEscalatorInnerHeight() { | |||
return new ComputedStyle(getEscalator().getTableWrapper()) | |||
.getHeightIncludingBorderPadding(); | |||
} | |||
/** | |||
* Grid does not support adding Widgets this way. | |||
* <p> |
@@ -1,5 +1,5 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.8.1//EN" "http://gwtproject.org/doctype/2.8.1/gwt-module.dtd"> | |||
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.8.2//EN" "http://www.gwtproject.org/doctype/2.8.2/gwt-module.dtd"> | |||
<module> | |||
<!-- This GWT module defines the Vaadin DefaultWidgetSet. This is the module | |||
you want to extend when creating an extended widget set, or when creating |
@@ -1,5 +1,5 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.8.1//EN" "http://gwtproject.org/doctype/2.8.1/gwt-module.dtd"> | |||
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.8.2//EN" "http://www.gwtproject.org/doctype/2.8.2/gwt-module.dtd"> | |||
<module> | |||
<!-- This GWT module inherits all Vaadin client side functionality modules. | |||
This is the module you want to inherit in your client side project to be |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>vaadin-compatibility-client-compiled</artifactId> | |||
<name>vaadin-compatibility-client-compiled</name> |
@@ -15,33 +15,16 @@ | |||
*/ | |||
package com.vaadin.osgi.compatibility.widgetset; | |||
import org.osgi.service.component.ComponentContext; | |||
import org.osgi.service.component.annotations.Activate; | |||
import org.osgi.service.component.annotations.Component; | |||
import org.osgi.service.component.annotations.Reference; | |||
import org.osgi.service.http.HttpService; | |||
import com.vaadin.osgi.resources.OsgiVaadinResources; | |||
import com.vaadin.osgi.resources.VaadinResourceService; | |||
@Component(immediate = true) | |||
public class CompatibilityWidgetsetContribution { | |||
private HttpService httpService; | |||
import com.vaadin.osgi.resources.OsgiVaadinWidgetset; | |||
@Component | |||
public class CompatibilityWidgetsetContribution implements OsgiVaadinWidgetset { | |||
private static final String WIDGETSET_NAME = "com.vaadin.v7.Vaadin7WidgetSet"; | |||
@Activate | |||
void startup(ComponentContext context) throws Exception { | |||
VaadinResourceService service = OsgiVaadinResources.getService(); | |||
service.publishWidgetset(WIDGETSET_NAME, httpService); | |||
} | |||
@Reference | |||
void setHttpService(HttpService httpService) { | |||
this.httpService = httpService; | |||
} | |||
void unsetHttpService(HttpService httpService) { | |||
this.httpService = null; | |||
@Override | |||
public String getName() { | |||
return WIDGETSET_NAME; | |||
} | |||
} |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>vaadin-compatibility-client</artifactId> | |||
<name>vaadin-compatibility-client</name> |
@@ -176,6 +176,28 @@ public class GridConnector extends AbstractHasComponentsConnector | |||
this.id = id; | |||
} | |||
/** | |||
* Creates and initializes a custom grid column with attributes of given state. | |||
* | |||
* @param state with attributes to initialize the column. | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
private CustomGridColumn(GridColumnState state) { | |||
this(state.id, (AbstractGridRendererConnector<Object>) state.rendererConnector); | |||
this.hidingToggleCaption = state.hidingToggleCaption; | |||
this.hidden = state.hidden; | |||
this.hidable = state.hidable; | |||
this.resizable = state.resizable; | |||
this.sortable = state.sortable; | |||
this.headerCaption = state.headerCaption == null ? "" : state.headerCaption; | |||
this.widthUser = state.width; | |||
this.minimumWidthPx = state.minWidth; | |||
this.maximumWidthPx = state.maxWidth; | |||
this.expandRatio = state.expandRatio; | |||
this.editable = state.editable; | |||
setEditorConnector((AbstractComponentConnector) state.editorConnector); | |||
} | |||
/** | |||
* Sets a new renderer for this column object | |||
* | |||
@@ -472,7 +494,8 @@ public class GridConnector extends AbstractHasComponentsConnector | |||
} else { | |||
getLogger().warning( | |||
"Visibility changed for a unknown column type in Grid: " | |||
+ column + ", type " + column.getClass()); | |||
+ column.toString() + ", type " | |||
+ column.getClass()); | |||
} | |||
} | |||
} | |||
@@ -544,9 +567,13 @@ public class GridConnector extends AbstractHasComponentsConnector | |||
if (spacerCellBorderHeights != null | |||
&& !getLayoutManager().isLayoutRunning() | |||
&& hasDetailsOpen(rowIndex)) { | |||
double height = getLayoutManager().getOuterHeightDouble( | |||
element) + spacerCellBorderHeights; | |||
getWidget().setDetailsHeight(rowIndex, height); | |||
// Measure and set details height if element is visible | |||
if (WidgetUtil.isDisplayed(element)) { | |||
double height = | |||
getLayoutManager().getOuterHeightDouble( | |||
element) + spacerCellBorderHeights; | |||
getWidget().setDetailsHeight(rowIndex, height); | |||
} | |||
} | |||
} | |||
}; | |||
@@ -890,13 +917,8 @@ public class GridConnector extends AbstractHasComponentsConnector | |||
// Remove old columns | |||
purgeRemovedColumns(); | |||
// Add new columns | |||
for (GridColumnState state : getState().columns) { | |||
if (!columnIdToColumn.containsKey(state.id)) { | |||
addColumnFromStateChangeEvent(state); | |||
} | |||
updateColumnFromStateChangeEvent(state); | |||
} | |||
// Update all columns | |||
updateColumnsFromState(); | |||
} | |||
if (stateChangeEvent.hasPropertyChanged("columnOrder")) { | |||
@@ -1099,37 +1121,33 @@ public class GridConnector extends AbstractHasComponentsConnector | |||
cell.setStyleName(cellState.styleName); | |||
} | |||
/** | |||
* Updates a column from a state change event. | |||
* | |||
* @param columnIndex | |||
* The index of the column to update | |||
*/ | |||
private void updateColumnFromStateChangeEvent(GridColumnState columnState) { | |||
CustomGridColumn column = columnIdToColumn.get(columnState.id); | |||
columnsUpdatedFromState = true; | |||
updateColumnFromState(column, columnState); | |||
columnsUpdatedFromState = false; | |||
} | |||
/** | |||
* Adds a new column to the grid widget from a state change event | |||
* Update columns from the current state. | |||
* | |||
* @param columnIndex | |||
* The index of the column, according to how it | |||
*/ | |||
private void addColumnFromStateChangeEvent(GridColumnState state) { | |||
private void updateColumnsFromState() { | |||
this.columnsUpdatedFromState = true; | |||
final List<Column<?, JsonObject>> columns = new ArrayList<Column<?, JsonObject>>(getState().columns.size()); | |||
for (String columnId : getState().columnOrder) { | |||
for (GridColumnState state : getState().columns) { | |||
if (state.id.equals(columnId)) { | |||
CustomGridColumn column = this.columnIdToColumn.get(state.id); | |||
if (column == null) { | |||
column = new CustomGridColumn(state); | |||
this.columnIdToColumn.put(state.id, column); | |||
this.columnOrder.add(state.id); | |||
columns.add(column); | |||
} else { | |||
updateColumnFromState(column, state); | |||
} | |||
} | |||
} | |||
} | |||
@SuppressWarnings("unchecked") | |||
CustomGridColumn column = new CustomGridColumn(state.id, | |||
((AbstractGridRendererConnector<Object>) state.rendererConnector)); | |||
columnIdToColumn.put(state.id, column); | |||
/* | |||
* Add column to grid. Reordering is handled as a separate problem. | |||
*/ | |||
getWidget().addColumn(column); | |||
columnOrder.add(state.id); | |||
final Column<?, JsonObject>[] columnArray = columns.toArray(new Column[0]); | |||
getWidget().addColumns(columnArray); | |||
this.columnsUpdatedFromState = false; | |||
} | |||
/** | |||
@@ -1148,7 +1166,8 @@ public class GridConnector extends AbstractHasComponentsConnector | |||
column.setMaximumWidth(state.maxWidth); | |||
column.setExpandRatio(state.expandRatio); | |||
assert state.rendererConnector instanceof AbstractGridRendererConnector : "GridColumnState.rendererConnector is invalid (not subclass of AbstractGridRendererConnector)"; | |||
assert state.rendererConnector instanceof AbstractGridRendererConnector : "GridColumnState.rendererConnector is invalid (not subclass of " | |||
+ "AbstractRendererConnector)"; | |||
column.setRenderer( | |||
(AbstractGridRendererConnector<Object>) state.rendererConnector); | |||
@@ -1322,6 +1341,7 @@ public class GridConnector extends AbstractHasComponentsConnector | |||
info.setContentMode(contentMode); | |||
return info; | |||
} | |||
@Override | |||
protected void sendContextClickEvent(MouseEventDetails details, | |||
EventTarget eventTarget) { |
@@ -25,6 +25,8 @@ import java.util.Locale; | |||
import java.util.Set; | |||
import java.util.logging.Logger; | |||
import com.google.gwt.animation.client.AnimationScheduler; | |||
import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; | |||
import com.google.gwt.aria.client.Roles; | |||
import com.google.gwt.core.client.JavaScriptObject; | |||
import com.google.gwt.core.client.Scheduler; | |||
@@ -56,6 +58,7 @@ import com.google.gwt.i18n.client.HasDirection.Direction; | |||
import com.google.gwt.user.client.Command; | |||
import com.google.gwt.user.client.DOM; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.Event.NativePreviewEvent; | |||
import com.google.gwt.user.client.Timer; | |||
import com.google.gwt.user.client.Window; | |||
import com.google.gwt.user.client.ui.Composite; | |||
@@ -355,8 +358,10 @@ public class VFilterSelect extends Composite | |||
private int popupOuterPadding = -1; | |||
private int topPosition; | |||
private int leftPosition; | |||
private final MouseWheeler mouseWheeler = new MouseWheeler(); | |||
private boolean scrollPending = false; | |||
/** | |||
* Default constructor | |||
@@ -444,12 +449,11 @@ public class VFilterSelect extends Composite | |||
getElement().setId("VAADIN_COMBOBOX_OPTIONLIST"); | |||
menu.setSuggestions(currentSuggestions); | |||
final int x = VFilterSelect.this.getAbsoluteLeft(); | |||
leftPosition = getDesiredLeftPosition(); | |||
topPosition = tb.getAbsoluteTop(); | |||
topPosition += tb.getOffsetHeight(); | |||
topPosition = getDesiredTopPosition(); | |||
setPopupPosition(x, topPosition); | |||
setPopupPosition(leftPosition, topPosition); | |||
int nullOffset = (nullSelectionAllowed | |||
&& "".equals(lastFilter) ? 1 : 0); | |||
@@ -496,6 +500,22 @@ public class VFilterSelect extends Composite | |||
}); | |||
} | |||
private native int toInt32(double val) | |||
/*-{ | |||
return val | 0; | |||
}-*/; | |||
private int getDesiredTopPosition() { | |||
return toInt32(WidgetUtil.getBoundingClientRect(tb.getElement()) | |||
.getBottom()) + Window.getScrollTop(); | |||
} | |||
private int getDesiredLeftPosition() { | |||
return toInt32(WidgetUtil | |||
.getBoundingClientRect(VFilterSelect.this.getElement()) | |||
.getLeft()); | |||
} | |||
/** | |||
* Should the next page button be visible to the user? | |||
* | |||
@@ -682,6 +702,47 @@ public class VFilterSelect extends Composite | |||
handleMouseDownEvent(event); | |||
} | |||
@Override | |||
protected void onPreviewNativeEvent(NativePreviewEvent event) { | |||
// Check all events outside the combobox to see if they scroll the | |||
// page. We cannot use e.g. Window.addScrollListener() because the | |||
// scrolled element can be at any level on the page. | |||
// Normally this is only called when the popup is showing, but make | |||
// sure we don't accidentally process all events when not showing. | |||
if (!scrollPending && isShowing() && !DOM.isOrHasChild( | |||
SuggestionPopup.this.getElement(), | |||
Element.as(event.getNativeEvent().getEventTarget()))) { | |||
if (getDesiredLeftPosition() != leftPosition | |||
|| getDesiredTopPosition() != topPosition) { | |||
updatePopupPositionOnScroll(); | |||
} | |||
} | |||
super.onPreviewNativeEvent(event); | |||
} | |||
/** | |||
* Make the popup follow the position of the ComboBox when the page is | |||
* scrolled. | |||
*/ | |||
private void updatePopupPositionOnScroll() { | |||
if (!scrollPending) { | |||
AnimationScheduler.get() | |||
.requestAnimationFrame(new AnimationCallback() { | |||
public void execute(double timestamp) { | |||
if (isShowing()) { | |||
leftPosition = getDesiredLeftPosition(); | |||
topPosition = getDesiredTopPosition(); | |||
setPopupPosition(leftPosition, topPosition); | |||
} | |||
scrollPending = false; | |||
} | |||
}); | |||
scrollPending = true; | |||
} | |||
} | |||
/** | |||
* Should paging be enabled. If paging is enabled then only a certain | |||
* amount of items are visible at a time and a scrollbar or buttons are |
@@ -220,6 +220,22 @@ public interface RowContainer { | |||
*/ | |||
public int getRowCount(); | |||
/** | |||
* This method calculates the current row count directly from the DOM. | |||
* <p> | |||
* While the container is stable, this value should equal to | |||
* {@link #getRowCount()}, but while row counts are being updated, these two | |||
* values might differ for a short while. | |||
* <p> | |||
* Any extra content, such as spacers for the body, should not be included | |||
* in this count. | |||
* | |||
* @since 8.7 | |||
* | |||
* @return the actual DOM count of rows | |||
*/ | |||
public int getDomRowCount(); | |||
/** | |||
* The default height of the rows in this RowContainer. | |||
* |
@@ -0,0 +1,84 @@ | |||
/* | |||
* Copyright 2000-2018 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.client.widget.escalator.events; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
/** | |||
* Event fired when a spacer element is hidden or shown in Escalator. | |||
* | |||
* @author Vaadin Ltd | |||
* @since 7.7.13 | |||
*/ | |||
public class SpacerVisibilityChangedEvent | |||
extends GwtEvent<SpacerVisibilityChangedHandler> { | |||
/** | |||
* Handler type. | |||
*/ | |||
public static final Type<SpacerVisibilityChangedHandler> TYPE = new Type<SpacerVisibilityChangedHandler>(); | |||
public static final Type<SpacerVisibilityChangedHandler> getType() { | |||
return TYPE; | |||
} | |||
private final int rowIndex; | |||
private final boolean visible; | |||
/** | |||
* Creates a spacer visibility changed event. | |||
* | |||
* @param rowIndex | |||
* index of row to which the spacer belongs | |||
* @param visible | |||
* {@code true} if the spacer element is shown, {@code false} if the | |||
* spacer element is hidden | |||
*/ | |||
public SpacerVisibilityChangedEvent(int rowIndex, boolean visible) { | |||
this.rowIndex = rowIndex; | |||
this.visible = visible; | |||
} | |||
/** | |||
* Gets the row index to which the spacer element belongs. | |||
* | |||
* @return the row index to which the spacer element belongs | |||
*/ | |||
public int getRowIndex() { | |||
return rowIndex; | |||
} | |||
/** | |||
* Gets whether the spacer element is displayed. | |||
* | |||
* @return {@code true} if the spacer element is shown, {@code false} if the | |||
* spacer element is hidden | |||
*/ | |||
public boolean isVisible() { | |||
return visible; | |||
} | |||
@Override | |||
public Type<SpacerVisibilityChangedHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
@Override | |||
protected void dispatch(SpacerVisibilityChangedHandler handler) { | |||
handler.onSpacerVisibilityChanged(this); | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
/* | |||
* Copyright 2000-2018 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.client.widget.escalator.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Event handler for a spacer visibility changed event. | |||
* | |||
* @author Vaadin Ltd | |||
* @since 7.7.13 | |||
*/ | |||
public interface SpacerVisibilityChangedHandler extends EventHandler { | |||
/** | |||
* Called when a spacer visibility changed event is fired, when a spacer's | |||
* visibility changes. | |||
* | |||
* @param event | |||
* the spacer visibility changed event | |||
*/ | |||
public void onSpacerVisibilityChanged(SpacerVisibilityChangedEvent event); | |||
} |
@@ -88,6 +88,7 @@ import com.vaadin.v7.client.widget.escalator.ScrollbarBundle.VerticalScrollbarBu | |||
import com.vaadin.v7.client.widget.escalator.Spacer; | |||
import com.vaadin.v7.client.widget.escalator.SpacerUpdater; | |||
import com.vaadin.v7.client.widget.escalator.events.RowHeightChangedEvent; | |||
import com.vaadin.v7.client.widget.escalator.events.SpacerVisibilityChangedEvent; | |||
import com.vaadin.v7.client.widget.grid.events.ScrollEvent; | |||
import com.vaadin.v7.client.widget.grid.events.ScrollHandler; | |||
import com.vaadin.v7.client.widgets.Escalator.JsniUtil.TouchHandlerBundle; | |||
@@ -1290,22 +1291,6 @@ public class Escalator extends Widget | |||
return rows; | |||
} | |||
/** | |||
* This method calculates the current row count directly from the DOM. | |||
* <p> | |||
* While Escalator is stable, this value should equal to | |||
* {@link #getRowCount()}, but while row counts are being updated, these | |||
* two values might differ for a short while. | |||
* <p> | |||
* Any extra content, such as spacers for the body, should not be | |||
* included in this count. | |||
* | |||
* @since 7.5.0 | |||
* | |||
* @return the actual DOM count of rows | |||
*/ | |||
public abstract int getDomRowCount(); | |||
/** | |||
* {@inheritDoc} | |||
* <p> | |||
@@ -4786,11 +4771,15 @@ public class Escalator extends Widget | |||
public void show() { | |||
getRootElement().getStyle().clearDisplay(); | |||
getDecoElement().getStyle().clearDisplay(); | |||
Escalator.this.fireEvent( | |||
new SpacerVisibilityChangedEvent(getRow(), true)); | |||
} | |||
public void hide() { | |||
getRootElement().getStyle().setDisplay(Display.NONE); | |||
getDecoElement().getStyle().setDisplay(Display.NONE); | |||
Escalator.this.fireEvent( | |||
new SpacerVisibilityChangedEvent(getRow(), false)); | |||
} | |||
/** |
@@ -114,6 +114,8 @@ import com.vaadin.v7.client.widget.escalator.Spacer; | |||
import com.vaadin.v7.client.widget.escalator.SpacerUpdater; | |||
import com.vaadin.v7.client.widget.escalator.events.RowHeightChangedEvent; | |||
import com.vaadin.v7.client.widget.escalator.events.RowHeightChangedHandler; | |||
import com.vaadin.v7.client.widget.escalator.events.SpacerVisibilityChangedEvent; | |||
import com.vaadin.v7.client.widget.escalator.events.SpacerVisibilityChangedHandler; | |||
import com.vaadin.v7.client.widget.grid.AutoScroller; | |||
import com.vaadin.v7.client.widget.grid.AutoScroller.AutoScrollerCallback; | |||
import com.vaadin.v7.client.widget.grid.AutoScroller.ScrollAxis; | |||
@@ -1316,6 +1318,23 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
private static final String ERROR_CLASS_NAME = "error"; | |||
private static final String NOT_EDITABLE_CLASS_NAME = "not-editable"; | |||
ScheduledCommand fieldFocusCommand = new ScheduledCommand() { | |||
private int count = 0; | |||
@Override | |||
public void execute() { | |||
Element focusedElement = WidgetUtil.getFocusedElement(); | |||
if (focusedElement == grid.getElement() | |||
|| focusedElement == Document.get().getBody() | |||
|| count > 2) { | |||
focusColumn(focusedColumnIndexDOM); | |||
} else { | |||
++count; | |||
Scheduler.get().scheduleDeferred(this); | |||
} | |||
} | |||
}; | |||
/** | |||
* A handler for events related to the Grid editor. Responsible for | |||
* opening, moving or closing the editor based on the received event. | |||
@@ -1875,7 +1894,11 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
} | |||
if (i == focusedColumnIndexDOM) { | |||
focusColumn(focusedColumnIndexDOM); | |||
if (BrowserInfo.get().isIE8()) { | |||
Scheduler.get().scheduleDeferred(fieldFocusCommand); | |||
} else { | |||
focusColumn(focusedColumnIndexDOM); | |||
} | |||
} | |||
} else { | |||
cell.addClassName(NOT_EDITABLE_CLASS_NAME); | |||
@@ -2116,8 +2139,20 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
private void updateHorizontalScrollPosition() { | |||
double scrollLeft = grid.getScrollLeft(); | |||
cellWrapper.getStyle().setLeft( | |||
frozenCellWrapper.getOffsetWidth() - scrollLeft, Unit.PX); | |||
int frozenWidth = frozenCellWrapper.getOffsetWidth(); | |||
double newLeft = frozenWidth - scrollLeft; | |||
cellWrapper.getStyle().setLeft(newLeft, Unit.PX); | |||
// sometimes focus handling twists the editor row out of alignment | |||
// with the grid itself and the position needs to be compensated for | |||
TableRowElement rowElement = grid.getEscalator().getBody() | |||
.getRowElement(grid.getEditor().getRow()); | |||
int rowLeft = rowElement.getAbsoluteLeft(); | |||
int editorLeft = cellWrapper.getAbsoluteLeft(); | |||
if (editorLeft != rowLeft + frozenWidth) { | |||
cellWrapper.getStyle().setLeft(newLeft + rowLeft - editorLeft, | |||
Unit.PX); | |||
} | |||
} | |||
/** | |||
@@ -2326,7 +2361,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
if (!Element.is(target)) { | |||
return null; | |||
} | |||
return WidgetUtil.findWidget(Element.as(target), Grid.class); | |||
return WidgetUtil.findWidget(Element.as(target), Grid.class, false); | |||
} | |||
/** | |||
@@ -2354,6 +2389,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
} else if (container == getGrid().escalator.getBody()) { | |||
section = Section.BODY; | |||
} | |||
doDispatch(handler, section); | |||
} | |||
} | |||
@@ -2392,7 +2428,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
if (!Element.is(target)) { | |||
return null; | |||
} | |||
return WidgetUtil.findWidget(Element.as(target), Grid.class); | |||
return WidgetUtil.findWidget(Element.as(target), Grid.class, false); | |||
} | |||
/** | |||
@@ -3148,7 +3184,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
if (!columns.contains(column)) { | |||
throw new IllegalArgumentException( | |||
"Given column is not a column in this grid. " + column); | |||
"Given column is not a column in this grid. " | |||
+ column.toString()); | |||
} | |||
if (!column.isSortable()) { | |||
@@ -3475,7 +3512,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
*/ | |||
final double widthPerRatio; | |||
int leftOver = 0; | |||
if (BrowserInfo.getBrowserString().contains("PhantomJS")) { | |||
if (BrowserInfo.get().isIE8() || BrowserInfo.get().isIE9() | |||
|| BrowserInfo.getBrowserString().contains("PhantomJS")) { | |||
// These browsers report subpixels as integers. this usually | |||
// results into issues.. | |||
widthPerRatio = (int) (pixelsToDistribute / totalRatios); | |||
@@ -4047,7 +4085,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
} | |||
private String createHTML(Column<?, T> column) { | |||
final StringBuilder buf = new StringBuilder(); | |||
final StringBuffer buf = new StringBuffer(); | |||
buf.append("<span class=\""); | |||
if (column.isHidden()) { | |||
buf.append("v-off"); | |||
@@ -4114,7 +4152,6 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
* on initialization, but not after that. | |||
*/ | |||
private DataSource<T> dataSource; | |||
private Registration changeHandler; | |||
/** | |||
* Currently available row range in DataSource. | |||
@@ -4330,10 +4367,12 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
&& rightBoundaryForDrag < dropMarkerLeft | |||
&& dropMarkerLeft <= escalator.getInnerWidth()) { | |||
dropMarkerLeft = rightBoundaryForDrag - dropMarkerWidthOffset; | |||
} else if ( | |||
} | |||
// Check if the drop marker shouldn't be shown at all | |||
dropMarkerLeft < frozenColumnsWidth || dropMarkerLeft > Math | |||
.min(rightBoundaryForDrag, escalator.getInnerWidth()) | |||
else if (dropMarkerLeft < frozenColumnsWidth | |||
|| dropMarkerLeft > Math.min(rightBoundaryForDrag, | |||
escalator.getInnerWidth()) | |||
|| dropMarkerLeft < 0) { | |||
dropMarkerLeft = -10000000; | |||
} | |||
@@ -4482,8 +4521,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
if (focusedColumnIndex == draggedColumnIndex) { | |||
// move with the dragged column | |||
int adjustedDropIndex = latestColumnDropIndex > draggedColumnIndex | |||
? latestColumnDropIndex - 1 | |||
: latestColumnDropIndex; | |||
? latestColumnDropIndex - 1 : latestColumnDropIndex; | |||
// remove hidden columns from indexing | |||
adjustedDropIndex = getVisibleColumns() | |||
.indexOf(getColumn(adjustedDropIndex)); | |||
@@ -4714,8 +4752,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
@Override | |||
public void render(RendererCellReference cell, Object data) { | |||
if (!warned && !(data instanceof String)) { | |||
getLogger().warning( | |||
Column.this + ": " + DEFAULT_RENDERER_WARNING); | |||
getLogger().warning(Column.this.toString() + ": " | |||
+ DEFAULT_RENDERER_WARNING); | |||
warned = true; | |||
} | |||
@@ -4736,33 +4774,65 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
private Grid<T> grid; | |||
/** | |||
* Width of column in pixels as {@link #setWidth(double)} has been | |||
* called | |||
* Width of column in pixels as {@link #setWidth(double)} has been called. | |||
*/ | |||
private double widthUser = GridConstants.DEFAULT_COLUMN_WIDTH_PX; | |||
protected double widthUser = GridConstants.DEFAULT_COLUMN_WIDTH_PX; | |||
/** | |||
* Renderer for rendering a value into the cell | |||
*/ | |||
private Renderer<? super C> bodyRenderer; | |||
private boolean sortable = false; | |||
/** | |||
* The sortable state of this column. | |||
*/ | |||
protected boolean sortable = false; | |||
private boolean editable = true; | |||
/** | |||
* The editable state of this column. | |||
*/ | |||
protected boolean editable = true; | |||
private boolean resizable = true; | |||
/** | |||
* The resizable state of this column. | |||
*/ | |||
protected boolean resizable = true; | |||
private boolean hidden = false; | |||
/** | |||
* The hidden state of this column. | |||
*/ | |||
protected boolean hidden = false; | |||
private boolean hidable = false; | |||
/** | |||
* The hidable state of this column. | |||
*/ | |||
protected boolean hidable = false; | |||
private String headerCaption = ""; | |||
/** | |||
* The header-caption of this column. | |||
*/ | |||
protected String headerCaption = ""; | |||
private String hidingToggleCaption = null; | |||
/** | |||
* The hiding-toggle-caption of this column. | |||
*/ | |||
protected String hidingToggleCaption = null; | |||
/** | |||
* The minimum width in pixels of this column. | |||
*/ | |||
protected double minimumWidthPx = GridConstants.DEFAULT_MIN_WIDTH; | |||
/** | |||
* The maximum width in pixels of this column. | |||
*/ | |||
protected double maximumWidthPx = GridConstants.DEFAULT_MAX_WIDTH; | |||
/** | |||
* The expand ratio of this column. | |||
*/ | |||
protected int expandRatio = GridConstants.DEFAULT_EXPAND_RATIO; | |||
private double minimumWidthPx = GridConstants.DEFAULT_MIN_WIDTH; | |||
private double maximumWidthPx = GridConstants.DEFAULT_MAX_WIDTH; | |||
private int expandRatio = GridConstants.DEFAULT_EXPAND_RATIO; | |||
/** | |||
* Constructs a new column with a simple TextRenderer. | |||
@@ -5644,7 +5714,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
if (renderer instanceof WidgetRenderer) { | |||
try { | |||
Widget w = WidgetUtil.findWidget( | |||
cell.getElement().getFirstChildElement(), null); | |||
cell.getElement().getFirstChildElement()); | |||
if (w != null) { | |||
// Logical detach | |||
@@ -5863,7 +5933,16 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
dragEnded(); | |||
col.setWidth(width); | |||
fireEvent(new ColumnResizeEvent<T>(col)); | |||
// Need to wait for column width recalculation | |||
// scheduled by setWidth() before firing the event | |||
Scheduler.get().scheduleDeferred( | |||
new ScheduledCommand() { | |||
@Override | |||
public void execute() { | |||
fireEvent(new ColumnResizeEvent<T>(col)); | |||
} | |||
}); | |||
} | |||
}; | |||
@@ -6201,29 +6280,6 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
fireEvent(new GridEnabledEvent(enabled)); | |||
} | |||
@Override | |||
public void setStylePrimaryName(String style) { | |||
super.setStylePrimaryName(style); | |||
escalator.setStylePrimaryName(style); | |||
editor.setStylePrimaryName(style); | |||
sidebar.setStylePrimaryName(style + "-sidebar"); | |||
sidebar.addStyleName("v-contextmenu"); | |||
String rowStyle = getStylePrimaryName() + "-row"; | |||
rowHasDataStyleName = rowStyle + "-has-data"; | |||
rowSelectedStyleName = rowStyle + "-selected"; | |||
rowStripeStyleName = rowStyle + "-stripe"; | |||
cellFocusStyleName = getStylePrimaryName() + "-cell-focused"; | |||
rowFocusStyleName = getStylePrimaryName() + "-row-focused"; | |||
if (isAttached()) { | |||
refreshHeader(); | |||
refreshBody(); | |||
refreshFooter(); | |||
} | |||
} | |||
/** | |||
* Sets the column resize mode to use. The default mode is | |||
* {@link ColumnResizeMode.ANIMATED}. | |||
@@ -6241,12 +6297,36 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
* {@link ColumnResizeMode.ANIMATED}. | |||
* | |||
* @return a ColumnResizeMode value | |||
* | |||
* @since 7.7.5 | |||
*/ | |||
public ColumnResizeMode getColumnResizeMode() { | |||
return columnResizeMode; | |||
} | |||
@Override | |||
public void setStylePrimaryName(String style) { | |||
super.setStylePrimaryName(style); | |||
escalator.setStylePrimaryName(style); | |||
editor.setStylePrimaryName(style); | |||
sidebar.setStylePrimaryName(style + "-sidebar"); | |||
sidebar.addStyleName("v-contextmenu"); | |||
String rowStyle = getStylePrimaryName() + "-row"; | |||
rowHasDataStyleName = rowStyle + "-has-data"; | |||
rowSelectedStyleName = rowStyle + "-selected"; | |||
rowStripeStyleName = rowStyle + "-stripe"; | |||
cellFocusStyleName = getStylePrimaryName() + "-cell-focused"; | |||
rowFocusStyleName = getStylePrimaryName() + "-row-focused"; | |||
if (isAttached()) { | |||
refreshHeader(); | |||
refreshBody(); | |||
refreshFooter(); | |||
} | |||
} | |||
/** | |||
* Creates the escalator updater used to update the header rows in this | |||
* grid. The updater is invoked when header rows or columns are added or | |||
@@ -6369,9 +6449,24 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
* the columns to add | |||
*/ | |||
public void addColumns(Column<?, T>... columns) { | |||
int count = getColumnCount(); | |||
final int count = getColumnCount(); | |||
for (Column<?, T> column : columns) { | |||
addColumn(column, count++); | |||
checkColumnIsValidToAdd(column, count); | |||
} | |||
addColumnsSkipSelectionColumnCheck(Arrays.asList(columns), count); | |||
} | |||
/** | |||
* Checks the given column is valid to add at the given index. | |||
*/ | |||
private void checkColumnIsValidToAdd(Column<?, T> column, int index) { | |||
if (column == this.selectionColumn) { | |||
throw new IllegalArgumentException( | |||
"The selection column may not be added manually"); | |||
} else if (this.selectionColumn != null && index == 0) { | |||
throw new IllegalStateException("A column cannot be inserted " | |||
+ "before the selection column"); | |||
} | |||
} | |||
@@ -6401,53 +6496,56 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
* and {@code index} is 0. | |||
*/ | |||
public <C extends Column<?, T>> C addColumn(C column, int index) { | |||
if (column == selectionColumn) { | |||
throw new IllegalArgumentException( | |||
"The selection column many " + "not be added manually"); | |||
} else if (selectionColumn != null && index == 0) { | |||
throw new IllegalStateException("A column cannot be inserted " | |||
+ "before the selection column"); | |||
} | |||
addColumnSkipSelectionColumnCheck(column, index); | |||
checkColumnIsValidToAdd(column, index); | |||
addColumnsSkipSelectionColumnCheck(Collections.singleton(column), index); | |||
return column; | |||
} | |||
private void addColumnSkipSelectionColumnCheck(Column<?, T> column, | |||
int index) { | |||
// Register column with grid | |||
columns.add(index, column); | |||
header.addColumn(column); | |||
footer.addColumn(column); | |||
private <C extends Column<?, T>> void addColumnsSkipSelectionColumnCheck(Collection<C> columnCollection, int index) { | |||
int visibleNewColumns = 0; | |||
int currentIndex = index; | |||
// Register this grid instance with the column | |||
((Column<?, T>) column).setGrid(this); | |||
//prevent updates of hiding toggles. | |||
//it will be updated finally all at once. | |||
this.columnHider.hidingColumn = true; | |||
// Grid knows about hidden columns, Escalator only knows about what is | |||
// visible so column indexes do not match | |||
if (!column.isHidden()) { | |||
int escalatorIndex = index; | |||
for (int existingColumn = 0; existingColumn < index; existingColumn++) { | |||
if (getColumn(existingColumn).isHidden()) { | |||
escalatorIndex--; | |||
} | |||
for (final Column<?, T> column : columnCollection) { | |||
// Register column with grid | |||
this.columns.add(currentIndex++, column); | |||
this.footer.addColumn(column); | |||
this.header.addColumn(column); | |||
// Register this grid instance with the column | |||
column.setGrid(this); | |||
if (!column.isHidden()) { | |||
visibleNewColumns++; | |||
} | |||
escalator.getColumnConfiguration().insertColumns(escalatorIndex, 1); | |||
} | |||
if (visibleNewColumns > 0) { | |||
final ColumnConfiguration columnConfiguration = this.escalator.getColumnConfiguration(); | |||
columnConfiguration.insertColumns(index, visibleNewColumns); | |||
} | |||
// Reapply column width | |||
column.reapplyWidth(); | |||
// Sink all renderer events | |||
Set<String> events = new HashSet<String>(); | |||
events.addAll(getConsumedEventsForRenderer(column.getRenderer())); | |||
for (final Column<?, T> column : columnCollection) { | |||
// Reapply column width | |||
column.reapplyWidth(); | |||
// Sink all renderer events | |||
final Set<String> events = new HashSet<String>(); | |||
events.addAll(getConsumedEventsForRenderer(column.getRenderer())); | |||
if (column.isHidable()) { | |||
columnHider.updateColumnHidable(column); | |||
if (column.isHidable()) { | |||
this.columnHider.updateColumnHidable(column); | |||
} | |||
sinkEvents(events); | |||
} | |||
sinkEvents(events); | |||
//now we do the update of the hiding toggles. | |||
this.columnHider.hidingColumn = false; | |||
this.columnHider.updateTogglesOrder(); | |||
refreshHeader(); | |||
this.header.updateColSpans(); | |||
this.footer.updateColSpans(); | |||
} | |||
private void sinkEvents(Collection<String> events) { | |||
@@ -6925,41 +7023,33 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
selectionModel.reset(); | |||
if (changeHandler != null) { | |||
changeHandler.remove(); | |||
changeHandler = null; | |||
if (this.dataSource != null) { | |||
this.dataSource.addDataChangeHandler((DataChangeHandler) null); | |||
} | |||
this.dataSource = dataSource; | |||
changeHandler = dataSource | |||
.addDataChangeHandler(new DataChangeHandler() { | |||
dataSource.addDataChangeHandler(new DataChangeHandler() { | |||
@Override | |||
public void dataUpdated(int firstIndex, int numberOfItems) { | |||
escalator.getBody().refreshRows(firstIndex, | |||
numberOfItems); | |||
escalator.getBody().refreshRows(firstIndex, numberOfItems); | |||
} | |||
@Override | |||
public void dataRemoved(int firstIndex, int numberOfItems) { | |||
escalator.getBody().removeRows(firstIndex, | |||
numberOfItems); | |||
Range removed = Range.withLength(firstIndex, | |||
numberOfItems); | |||
escalator.getBody().removeRows(firstIndex, numberOfItems); | |||
Range removed = Range.withLength(firstIndex, numberOfItems); | |||
cellFocusHandler.rowsRemovedFromBody(removed); | |||
} | |||
@Override | |||
public void dataAdded(int firstIndex, int numberOfItems) { | |||
escalator.getBody().insertRows(firstIndex, | |||
numberOfItems); | |||
Range added = Range.withLength(firstIndex, | |||
numberOfItems); | |||
escalator.getBody().insertRows(firstIndex, numberOfItems); | |||
Range added = Range.withLength(firstIndex, numberOfItems); | |||
cellFocusHandler.rowsAddedToBody(added); | |||
} | |||
@Override | |||
public void dataAvailable(int firstIndex, | |||
int numberOfItems) { | |||
public void dataAvailable(int firstIndex, int numberOfItems) { | |||
currentDataAvailable = Range.withLength(firstIndex, | |||
numberOfItems); | |||
fireEvent(new DataAvailableEvent(currentDataAvailable)); | |||
@@ -6971,31 +7061,27 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
int oldSize = body.getRowCount(); | |||
// Hide all details. | |||
Set<Integer> oldDetails = new HashSet<Integer>( | |||
visibleDetails); | |||
Set<Integer> oldDetails = new HashSet<Integer>(visibleDetails); | |||
for (int i : oldDetails) { | |||
setDetailsVisible(i, false); | |||
} | |||
if (newSize > oldSize) { | |||
body.insertRows(oldSize, newSize - oldSize); | |||
cellFocusHandler.rowsAddedToBody(Range | |||
.withLength(oldSize, newSize - oldSize)); | |||
cellFocusHandler.rowsAddedToBody( | |||
Range.withLength(oldSize, newSize - oldSize)); | |||
} else if (newSize < oldSize) { | |||
body.removeRows(newSize, oldSize - newSize); | |||
cellFocusHandler.rowsRemovedFromBody(Range | |||
.withLength(newSize, oldSize - newSize)); | |||
cellFocusHandler.rowsRemovedFromBody( | |||
Range.withLength(newSize, oldSize - newSize)); | |||
} | |||
if (newSize > 0) { | |||
Range visibleRowRange = escalator | |||
.getVisibleRowRange(); | |||
dataSource.ensureAvailability( | |||
visibleRowRange.getStart(), | |||
Range visibleRowRange = escalator.getVisibleRowRange(); | |||
dataSource.ensureAvailability(visibleRowRange.getStart(), | |||
visibleRowRange.length()); | |||
} else { | |||
// We won't expect any data more data updates, so | |||
// just make | |||
// We won't expect any data more data updates, so just make | |||
// the bookkeeping happy | |||
dataAvailable(0, 0); | |||
} | |||
@@ -7466,7 +7552,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
} | |||
private boolean isElementInChildWidget(Element e) { | |||
Widget w = WidgetUtil.findWidget(e, null); | |||
Widget w = WidgetUtil.findWidget(e); | |||
if (w == this) { | |||
return false; | |||
@@ -7853,7 +7939,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
cellFocusHandler.offsetRangeBy(1); | |||
selectionColumn = new SelectionColumn(selectColumnRenderer); | |||
addColumnSkipSelectionColumnCheck(selectionColumn, 0); | |||
addColumnsSkipSelectionColumnCheck(Collections.singleton(selectionColumn), 0); | |||
selectionColumn.setEnabled(isEnabled()); | |||
selectionColumn.initDone(); | |||
@@ -8439,6 +8525,19 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
return escalator.addHandler(handler, RowHeightChangedEvent.TYPE); | |||
} | |||
/** | |||
* Adds a spacer visibility changed handler to the underlying escalator. | |||
* | |||
* @param handler | |||
* the handler to be called when a spacer's visibility changes | |||
* @return the registration object with which the handler can be removed | |||
* @since 7.7.13 | |||
*/ | |||
public HandlerRegistration addSpacerVisibilityChangedHandler( | |||
SpacerVisibilityChangedHandler handler) { | |||
return escalator.addHandler(handler, SpacerVisibilityChangedEvent.TYPE); | |||
} | |||
/** | |||
* Adds a low-level DOM event handler to this Grid. The handler is inserted | |||
* into the given position in the list of handlers. The handlers are invoked | |||
@@ -8830,7 +8929,6 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
for (int row : details) { | |||
setDetailsVisible(row, false); | |||
} | |||
super.onDetach(); | |||
} | |||
@@ -8948,17 +9046,17 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, | |||
* @param parent | |||
* The parent to set | |||
*/ | |||
private static final native void setParent(Widget widget, Grid<?> parent) | |||
private static native final void setParent(Widget widget, Grid<?> parent) | |||
/*-{ | |||
widget.@com.google.gwt.user.client.ui.Widget::setParent(Lcom/google/gwt/user/client/ui/Widget;)(parent); | |||
}-*/; | |||
private static final native void onAttach(Widget widget) | |||
private static native final void onAttach(Widget widget) | |||
/*-{ | |||
widget.@Widget::onAttach()(); | |||
}-*/; | |||
private static final native void onDetach(Widget widget) | |||
private static native final void onDetach(Widget widget) | |||
/*-{ | |||
widget.@Widget::onDetach()(); | |||
}-*/; |
@@ -1,5 +1,5 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.8.1//EN" "http://gwtproject.org/doctype/2.8.1/gwt-module.dtd"> | |||
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.8.2//EN" "http://www.gwtproject.org/doctype/2.8.2/gwt-module.dtd"> | |||
<module> | |||
<!-- Hint for WidgetSetBuilder not to automatically update the file --> | |||
<!-- WS Compiler: manually edited --> |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>vaadin-compatibility-server-gae</artifactId> | |||
<name>vaadin-compatibility-server-gae</name> |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>vaadin-compatibility-server</artifactId> | |||
<name>vaadin-compatibility-server</name> | |||
@@ -52,6 +52,18 @@ | |||
<artifactId>hsqldb</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>javax.portlet</groupId> | |||
<artifactId>portlet-api</artifactId> | |||
<version>${javax.portlet.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.vaadin.external.atmosphere</groupId> | |||
<artifactId>atmosphere-runtime</artifactId> | |||
<version>${atmosphere.runtime.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- Bean Validation API --> | |||
<dependency> | |||
<groupId>javax.validation</groupId> |
@@ -441,7 +441,7 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE | |||
public boolean removeContainerProperty(Object propertyId) | |||
throws UnsupportedOperationException { | |||
throw new UnsupportedOperationException( | |||
"Removing container properties not supported. Override the addContainerProperty() method if required."); | |||
"Removing container properties not supported. Override the removeContainerProperty() method if required."); | |||
} | |||
// ItemSetChangeNotifier |
@@ -1551,7 +1551,8 @@ public abstract class AbstractField<T> extends AbstractLegacyComponent | |||
* A ready-made {@link ShortcutListener} that focuses the given | |||
* {@link Focusable} (usually a {@link Field}) when the keyboard shortcut is | |||
* invoked. | |||
* | |||
* | |||
* @deprecated Replaced in 8.0 with {@link com.vaadin.event.FocusShortcut} | |||
*/ | |||
@Deprecated | |||
public static class FocusShortcut extends ShortcutListener { |
@@ -0,0 +1,5 @@ | |||
package com.vaadin.v7.tests.server; | |||
public class ClassesSerializableTest extends com.vaadin.tests.server.ClassesSerializableTest { | |||
} |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>vaadin-compatibility-shared</artifactId> | |||
<name>vaadin-compatibility-shared</name> |
@@ -1,189 +0,0 @@ | |||
/* | |||
* Copyright 2000-2018 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.shared.ui.grid; | |||
import java.io.Serializable; | |||
import java.util.Comparator; | |||
import com.vaadin.shared.Connector; | |||
/** | |||
* A description of an indexing modification for a connector. This is used by | |||
* Grid for internal bookkeeping updates. | |||
* | |||
* @since 7.5.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class DetailsConnectorChange implements Serializable { | |||
public static final Comparator<DetailsConnectorChange> REMOVED_FIRST_COMPARATOR = new Comparator<DetailsConnectorChange>() { | |||
@Override | |||
public int compare(DetailsConnectorChange a, DetailsConnectorChange b) { | |||
boolean deleteA = a.getNewIndex() == null; | |||
boolean deleteB = b.getNewIndex() == null; | |||
if (deleteA && !deleteB) { | |||
return -1; | |||
} else if (!deleteA && deleteB) { | |||
return 1; | |||
} else { | |||
return 0; | |||
} | |||
} | |||
}; | |||
private Connector connector; | |||
private Integer oldIndex; | |||
private Integer newIndex; | |||
private boolean shouldStillBeVisible; | |||
/** Create a new connector index change. */ | |||
public DetailsConnectorChange() { | |||
} | |||
/** | |||
* Convenience constructor for setting all the fields in one line. | |||
* <p> | |||
* Calling this constructor will also assert that the state of the pojo is | |||
* consistent by internal assumptions. | |||
* | |||
* @param connector | |||
* the changed connector | |||
* @param oldIndex | |||
* the old index | |||
* @param newIndex | |||
* the new index | |||
* @param shouldStillBeVisible | |||
* details should be visible regardless of {@code connector} | |||
*/ | |||
public DetailsConnectorChange(Connector connector, Integer oldIndex, | |||
Integer newIndex, boolean shouldStillBeVisible) { | |||
this.connector = connector; | |||
this.oldIndex = oldIndex; | |||
this.newIndex = newIndex; | |||
this.shouldStillBeVisible = shouldStillBeVisible; | |||
assert assertStateIsOk(); | |||
} | |||
private boolean assertStateIsOk() { | |||
boolean connectorAndNewIndexIsNotNull = connector != null | |||
&& newIndex != null; | |||
boolean connectorAndNewIndexIsNullThenOldIndexIsSet = connector == null | |||
&& newIndex == null && oldIndex != null; | |||
assert (connectorAndNewIndexIsNotNull | |||
|| connectorAndNewIndexIsNullThenOldIndexIsSet) : "connector: " | |||
+ nullityString(connector) + ", oldIndex: " | |||
+ nullityString(oldIndex) + ", newIndex: " | |||
+ nullityString(newIndex); | |||
return true; | |||
} | |||
private static String nullityString(Object object) { | |||
return object == null ? "null" : "non-null"; | |||
} | |||
/** | |||
* Gets the old index for the connector. | |||
* <p> | |||
* If <code>null</code>, the connector is recently added. This means that | |||
* {@link #getConnector()} is expected not to return <code>null</code>. | |||
* | |||
* @return the old index for the connector | |||
*/ | |||
public Integer getOldIndex() { | |||
assert assertStateIsOk(); | |||
return oldIndex; | |||
} | |||
/** | |||
* Gets the new index for the connector. | |||
* <p> | |||
* If <code>null</code>, the connector should be removed. This means that | |||
* {@link #getConnector()} is expected to return <code>null</code> as well. | |||
* | |||
* @return the new index for the connector | |||
*/ | |||
public Integer getNewIndex() { | |||
assert assertStateIsOk(); | |||
return newIndex; | |||
} | |||
/** | |||
* Gets the changed connector. | |||
* | |||
* @return the changed connector. Might be <code>null</code> | |||
*/ | |||
public Connector getConnector() { | |||
assert assertStateIsOk(); | |||
return connector; | |||
} | |||
/** | |||
* Sets the changed connector. | |||
* | |||
* @param connector | |||
* the changed connector. May be <code>null</code> | |||
*/ | |||
public void setConnector(Connector connector) { | |||
this.connector = connector; | |||
} | |||
/** | |||
* Sets the old index. | |||
* | |||
* @param oldIndex | |||
* the old index. May be <code>null</code> if a new connector is | |||
* being inserted | |||
*/ | |||
public void setOldIndex(Integer oldIndex) { | |||
this.oldIndex = oldIndex; | |||
} | |||
/** | |||
* Sets the new index. | |||
* | |||
* @param newIndex | |||
* the new index. May be <code>null</code> if a connector is | |||
* being removed | |||
*/ | |||
public void setNewIndex(Integer newIndex) { | |||
this.newIndex = newIndex; | |||
} | |||
/** | |||
* Checks whether whether the details should remain open, even if connector | |||
* might be <code>null</code>. | |||
* | |||
* @return <code>true</code> if the details should remain open, even if | |||
* connector might be <code>null</code> | |||
*/ | |||
public boolean isShouldStillBeVisible() { | |||
return shouldStillBeVisible; | |||
} | |||
/** | |||
* Sets whether the details should remain open, even if connector might be | |||
* <code>null</code>. | |||
* | |||
* @param shouldStillBeVisible | |||
* <code>true</code> if the details should remain open, even if | |||
* connector might be <code>null</code> | |||
*/ | |||
public void setShouldStillBeVisible(boolean shouldStillBeVisible) { | |||
this.shouldStillBeVisible = shouldStillBeVisible; | |||
} | |||
} |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>vaadin-compatibility-themes</artifactId> | |||
<name>vaadin-compatibility-themes</name> |
@@ -15,35 +15,27 @@ | |||
*/ | |||
package com.vaadin.osgi.compatibility.themes; | |||
import org.osgi.service.component.annotations.Activate; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.osgi.service.component.annotations.Component; | |||
import org.osgi.service.component.annotations.Reference; | |||
import org.osgi.service.http.HttpService; | |||
import com.vaadin.osgi.resources.OsgiVaadinResources; | |||
import com.vaadin.osgi.resources.VaadinResourceService; | |||
import com.vaadin.osgi.resources.OsgiVaadinContributor; | |||
import com.vaadin.osgi.resources.OsgiVaadinResource; | |||
import com.vaadin.osgi.resources.OsgiVaadinTheme; | |||
@Component(immediate = true) | |||
public class LegacyThemeContributions { | |||
@Component | |||
public class LegacyThemeContributions implements OsgiVaadinContributor { | |||
private static final String[] LEGACY_THEMES = { "base", "chameleon", | |||
"reindeer", "runo" }; | |||
private HttpService httpService; | |||
@Activate | |||
void startup() throws Exception { | |||
VaadinResourceService service = OsgiVaadinResources.getService(); | |||
for (String themeName : LEGACY_THEMES) { | |||
service.publishTheme(themeName, httpService); | |||
@Override | |||
public List<OsgiVaadinResource> getContributions() { | |||
final List<OsgiVaadinResource> contributions = new ArrayList<>( | |||
LEGACY_THEMES.length); | |||
for (final String theme : LEGACY_THEMES) { | |||
contributions.add(OsgiVaadinTheme.create(theme)); | |||
} | |||
} | |||
@Reference | |||
void setHttpService(HttpService httpService) { | |||
this.httpService = httpService; | |||
} | |||
void unsetHttpService(HttpService httpService) { | |||
this.httpService = null; | |||
return contributions; | |||
} | |||
} |
@@ -20,14 +20,13 @@ need to register in the website to obtain a key. | |||
You can get license keys from link:https://vaadin.com/pro/licenses[vaadin.com/pro/licenses]. | |||
. Select in the Vaadin website "My Account > Licenses" or directly | |||
[menuchoice]#Licenses# if you are a Pro Tools subscriber. | |||
. On the Vaadin website, click on your profile in the top right, then select "My Services > Licenses". | |||
+ | |||
image::img/cval-pro-licenses-3.png[width=80%, scaledwidth=100%] | |||
image::img/cval-pro-licenses-4.png[width=80%, scaledwidth=100%] | |||
. Click on a license key to obtain the purchased or trial key. | |||
+ | |||
image::img/cval-pro-licenses-code.png[width=80%, scaledwidth=100%] | |||
image::img/cval-pro-licenses-code-2.png[width=80%, scaledwidth=100%] | |||
[[addons.cval.installing]] | |||
@@ -62,7 +61,7 @@ usually with a [literal]#++-D++# option. For example, on the command-line: | |||
[prompt]#$# [command]#java# -Dvaadin.[replaceable]##<product>##.developer.license=[replaceable]#L1cen5e-c0de# ... | |||
---- | |||
where the [literal]`<product>` is the product ID, such as `charts`, `spreadsheet`, `designer`, or `testbench`. | |||
where the [literal]`<product>` is the product ID, such as `charts`, `spreadsheet`, or `testbench`. | |||
[[addons.cval.systemproperty.environments]] | |||
=== Passing License Key in Different Environments | |||
@@ -105,7 +104,7 @@ Apache Maven:: If building the project with Apache Maven, you can pass the licen | |||
[prompt]#$# [command]#mvn# -Dvaadin.[replaceable]##<product>##.developer.license=[replaceable]##L1cen5e-c0de## package | |||
---- | |||
+ | |||
where the [literal]`<product>` is the product ID, such as `charts`, `spreadsheet`, `designer`, or `testbench`. | |||
where the [literal]`<product>` is the product ID, such as `charts`, `spreadsheet`, or `testbench`. | |||
Continuous Integration Systems:: In CIS systems, you can pass the license key to build runners as a system | |||
property in the build configuration. However, this only passes it to a runner. |
@@ -78,14 +78,14 @@ image::img/directory-activate.png[width=50%, scaledwidth=70%] | |||
+ | |||
For official Vaadin add-ons, see <<addons-cval#addons.cval, "Installing Commercial Vaadin Add-on License">> for more information. | |||
. _In Vaadin 7.6 and older_: You need to compile the widget set as described in <<addons.maven.compiling>>. | |||
. _In Vaadin 7.6 and older_: You need to configure the widget set as described in <<addons.maven.compiling>>. | |||
[[addons.maven.compiling]] | |||
== Compiling the Application Widget Set | |||
== Configuring the Application Widget Set | |||
[NOTE] | |||
==== | |||
The widget set is automatically compiled in Vaadin 7.7 and later. | |||
The widget set is automatically configured in Vaadin 7.7 and later. | |||
The plugin will attempt to detect any add-ons that need the widget set to be compiled. | |||
Just note that it can take a bit time to compile. | |||
@@ -94,10 +94,10 @@ To speed up, instead of compiling it locally, you can also use a public cloud se | |||
See <<addons.maven.modes>> for instructions. | |||
==== | |||
In projects that use Vaadin 7.6 or older, you need to manually compile the widget set as follows. | |||
In projects that use Vaadin 7.6 or older, you need to manually configure the widget set as follows. | |||
[[addons.maven.widgetset]] | |||
=== Enabling Widget Set Compilation | |||
=== Configuring Widget Set Compilation | |||
Compiling the widget set in Maven projects requires the Vaadin Maven plugin. | |||
It is included in Maven projects created with a current Vaadin archetype. |
@@ -183,7 +183,7 @@ compatible browser, such as Mozilla Firefox 3.6 or newer. | |||
== Mobile Drag And Drop Support | |||
The HTML 5 Drag and Drop API is not yet supported by mobile browsers. To enable HTML5 DnD support on mobile devices, we have included | |||
an link:https://github.com/timruffles/ios-html5-drag-drop-shim/tree/rewrite:[external Polyfill]. Please note that this Polyfill is under the BSD 2 License. | |||
an link:https://github.com/timruffles/mobile-drag-drop[external Polyfill]. Please note that this Polyfill is under the BSD 2 License. | |||
By default, the mobile DnD support is disabled, but you can enable it any time for a [classname]#UI#. Starting from the request where the support was enabled, all the added [classname]#DragSourceExtension#, [classname]#DropTargetExtension# and their subclasses will also work on mobile devices for that UI. The Polyfill is only loaded when the user is using a touch device. | |||
@@ -420,7 +420,7 @@ When dropping on top of the grid's header or footer, the drop location will be ` | |||
A drop target Grid's body has the style name `v-grid-body-droptarget` to indicate that it is a potential target for data to be dropped. | |||
When dragging data over a drop target Grid's row, depending on the drop mode and the mouse position relative to the row, a style name is applied to the row or to the grid body to indicate the drop location. | |||
When dragging on top of a row, `v-grid-row-drag-center` indicates ON_TOP, `v-grid-row-drag-top` indicates ABOVE and `v-grid-row-drag-bottom` indicates BELOW locations. When dragging on top of an empty grid, or when the drop location is ON_TOP and dragged below the last row in grid (and there is empty space visible), the `v-grid-body-body-drag-top` style is applied to the `v-grid-tablewrapper` element which surrounds the grid header, body and footer. | |||
When dragging on top of a row, `v-grid-row-drag-center` indicates ON_TOP, `v-grid-row-drag-top` indicates ABOVE and `v-grid-row-drag-bottom` indicates BELOW locations. When dragging on top of an empty grid, or when the drop location is ON_TOP and dragged below the last row in grid (and there is empty space visible), the `v-grid-body-drag-top` style is applied to the `v-grid-tablewrapper` element which surrounds the grid header, body and footer. | |||
(((range="endofrange", startref="term.advanced.dragndrop"))) | |||
@@ -21,11 +21,13 @@ Vaadin application for OSGi should be a valid bundle, i.e. it should be packaged | |||
The easiest way to convert regular maven-based Vaadin application into a valid OSGi bundle consists of five steps: | |||
* Change packaging type to `jar` in your `pom.xml`: | |||
[source, xml] | |||
---- | |||
<packaging>jar</packaging> | |||
---- | |||
* Change the scope for all vaadin dependencies from default to `provided`, like this: | |||
[source, xml] | |||
---- | |||
<dependency> | |||
@@ -35,6 +37,7 @@ The easiest way to convert regular maven-based Vaadin application into a valid O | |||
</dependency> | |||
---- | |||
* Add OSGi-related dependencies to the project | |||
[source, xml] | |||
---- | |||
<groupId>com.vaadin</groupId> | |||
@@ -62,6 +65,7 @@ The easiest way to convert regular maven-based Vaadin application into a valid O | |||
</dependency> | |||
---- | |||
* Setup necessary plugins for building the project: | |||
[source, xml] | |||
---- | |||
<build> | |||
@@ -93,6 +97,7 @@ The easiest way to convert regular maven-based Vaadin application into a valid O | |||
</build> | |||
---- | |||
* Add bundle script (`bnd.bnd`) into the project root folder: | |||
[source, text] | |||
---- | |||
Bundle-Name: ${project.name} |
@@ -50,11 +50,10 @@ image::img/shortcut-defaultbutton.png[] | |||
[[advanced.shortcuts.focus]] | |||
== Field Focus Shortcuts | |||
You can define a shortcut key that sets the focus to a field component (any | |||
component that inherits [classname]#AbstractField#) by adding a | |||
[classname]#FocusShortcut# as a shortcut listener to the field. | |||
You can define a shortcut key that sets the focus to any focusable component (implements [interface]#Focusable#), usually field components, by adding a | |||
[interface]#FocusShortcut# as a shortcut listener to the component. | |||
The constructor of the [classname]#FocusShortcut# takes the field component as | |||
The constructor of the [classname]#FocusShortcut# takes the focusable component as | |||
its first parameter, followed by the key code, and an optional list of modifier | |||
keys, as listed in <<advanced.shortcuts.keycodes>>. | |||
@@ -64,7 +63,7 @@ keys, as listed in <<advanced.shortcuts.keycodes>>. | |||
// A field with Alt+N bound to it | |||
TextField name = new TextField("Name (Alt+N)"); | |||
name.addShortcutListener( | |||
new AbstractField.FocusShortcut(name, KeyCode.N, | |||
new FocusShortcut(name, KeyCode.N, | |||
ModifierKey.ALT)); | |||
layout.addComponent(name); | |||
---- | |||
@@ -78,7 +77,7 @@ key is indicated with an ampersand ( [literal]#++&++#). | |||
// A field with Alt+A bound to it, using shorthand notation | |||
TextField address = new TextField("Address (Alt+A)"); | |||
address.addShortcutListener( | |||
new AbstractField.FocusShortcut(address, "&Address")); | |||
new FocusShortcut(address, "&Address")); | |||
---- | |||
This is especially useful for internationalization, so that you can determine | |||
@@ -269,7 +268,7 @@ following defines a kbd:[Ctrl+Shift+N] key combination for a shortcut. | |||
---- | |||
TextField name = new TextField("Name (Ctrl+Shift+N)"); | |||
name.addShortcutListener( | |||
new AbstractField.FocusShortcut(name, KeyCode.N, | |||
new FocusShortcut(name, KeyCode.N, | |||
ModifierKey.CTRL, | |||
ModifierKey.SHIFT)); | |||
---- |
@@ -62,8 +62,7 @@ along with the file to ensure the browser doesn't try to open the file | |||
even if it's is a file type that the browser knows how to deal with. | |||
[[lazily-determine-the-content-and-the-name-of-the-file-being-server]] | |||
Lazily determine the content and the name of the file being server | |||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |||
==== Lazily determine the content and the name of the file being server | |||
One can lazily determine the content of the file using a | |||
`StreamResource`. Yet the name of the file that is going to be | |||
@@ -110,3 +109,28 @@ public class OnDemandFileDownloader extends FileDownloader { | |||
} | |||
} | |||
.... | |||
[[lazily-determine-the-content-and-the-name-of-the-file-being-server]] | |||
==== Cancelled downloads | |||
Since downloadable files may be quite big, and the download process may take time, the user might decide to | |||
cancel the download process. In this case `IOException` may be thrown by the web server. That | |||
does not mean something went wrong with the application, but the user pressed `Cancel` button during download. To prevent the exception to be logged, you can catch and ignore it as here: | |||
```java | |||
public class IgnoreCancelDownloader extends FileDownloader { | |||
... | |||
@Override | |||
public boolean handleConnectorRequest(final VaadinRequest request, final VaadinResponse response, final String path) { | |||
try { | |||
return super.handleConnectorRequest(request, response, path); | |||
} catch (final IOException ignored) { | |||
return true; | |||
} | |||
} | |||
} | |||
``` | |||
Note that the exception is a sublclass of `IOException`, but the particular class depends on the web container. |
@@ -134,7 +134,7 @@ follows: | |||
HtmlEmail email = new HtmlEmail(); | |||
email.setHostName("localhost"); | |||
email.setSmtpPort(9090); | |||
email.setAuthentication()"sender@test.com", "password"); | |||
email.setAuthentication("sender@test.com", "password"); | |||
.... | |||
Or if you want to use Gmail: |
@@ -8,8 +8,8 @@ layout: page | |||
= Using RPC from JavaScript | |||
This tutorial continues where | |||
link:IntegratingAJavaScriptComponent.asciidoc[Integrating a JavaScript | |||
component] ended. We will now add RPC functionality to the JavaScript | |||
<<IntegratingAJavaScriptComponent.asciidoc#,"Integrating a JavaScript | |||
component">> ended. We will now add RPC functionality to the JavaScript | |||
Flot component. RPC can be used in the same way as with ordinary GWT | |||
components. | |||
@@ -22,4 +22,4 @@ example which can be configured: | |||
* `vaadin.servlet.closeIdleSessions=true` | |||
For full list of available properties, see | |||
https://github.com/vaadin/spring/blob/master/vaadin-spring-boot/src/main/java/com/vaadin/spring/boot/internal/VaadinServletConfigurationProperties.java[VaadinServletConfigurationProperties]. | |||
https://github.com/vaadin/spring/blob/3.0/vaadin-spring-boot/src/main/java/com/vaadin/spring/boot/internal/VaadinServletConfigurationProperties.java[VaadinServletConfigurationProperties]. |
@@ -20,8 +20,8 @@ widget set, you should normally inherit the [classname]#DefaultWidgetSet#. | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE module PUBLIC | |||
"-//Google Inc.//DTD Google Web Toolkit 2.8.1//EN" | |||
"http://gwtproject.org/doctype/2.8.1/gwt-module.dtd"> | |||
"-//Google Inc.//DTD Google Web Toolkit 2.8.2//EN" | |||
"http://www.gwtproject.org/doctype/2.8.2/gwt-module.dtd"> | |||
<module> | |||
<!-- Inherit the default widget set --> |
@@ -12,7 +12,7 @@ starts, much like the [methodname]#init()# method in server-side Vaadin UIs. | |||
Consider the following application: | |||
[source, java] | |||
---- | |||
package com.example.myapp.client; | |||
@@ -51,7 +51,7 @@ configuration, in a client-side module descriptor, described in | |||
Module Descriptor">>. The descriptor is an XML file with suffix | |||
[filename]#.gwt.xml#. | |||
[source, xml] | |||
---- | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE module PUBLIC |
@@ -11,7 +11,7 @@ You can load the JavaScript code of a client-side application in an HTML __host | |||
page__ by including it with a [literal]#++<script>++# tag, for example as | |||
follows: | |||
[source, html] | |||
---- | |||
<html xmlns="http://www.w3.org/1999/xhtml"> | |||
<head> |
@@ -30,7 +30,7 @@ the JavaScript of the compiled module is loaded in the browser. | |||
Consider the following client-side application: | |||
[source, java] | |||
---- | |||
public class HelloWorld implements EntryPoint { | |||
@Override |
@@ -29,7 +29,7 @@ modify the element as needed. | |||
For example, [classname]#TextRenderer# is implemented simply as follows: | |||
[source, java] | |||
---- | |||
public class TextRenderer implements Renderer<String> { | |||
@Override | |||
@@ -44,7 +44,7 @@ The server-side renderer API should extend [classname]#AbstractRenderer# or | |||
[classname]#ClickableRenderer# with the data type accepted by the renderer. The | |||
data type also must be given for the superclass constructor. | |||
[source, java] | |||
---- | |||
public class TextRenderer extends AbstractRenderer<String> { | |||
public TextRenderer() { | |||
@@ -56,7 +56,7 @@ public class TextRenderer extends AbstractRenderer<String> { | |||
The client-side and server-side renderer need to be connected with a connector | |||
extending from [classname]#AbstractRendererConnector#. | |||
[source, java] | |||
---- | |||
@Connect(com.vaadin.ui.renderer.TextRenderer.class) | |||
public class TextRendererConnector |
@@ -13,7 +13,7 @@ widgets have somewhat different feature set from the GWT widgets and are | |||
foremost intended for integration with the server-side components, but some may | |||
prove useful for client-side applications as well. | |||
[source, java] | |||
---- | |||
public class MyEntryPoint implements EntryPoint { | |||
@Override |
@@ -55,5 +55,4 @@ image::img/checkbox-example1.png[width=35%, scaledwidth=50%] | |||
The top-level element of a [classname]#CheckBox# has the | |||
[literal]#++v-checkbox++# style. It contains two sub-elements: the actual check | |||
box [literal]#++input++# element and the [literal]#++label++# element. If you | |||
want to have the label on the left, you can change the positions with "[literal]#++direction: rtl++#" for the top element. | |||
box [literal]#++input++# element and the [literal]#++label++# element. |
@@ -533,18 +533,19 @@ Formats a column with the [classname]#LocalDate# type. | |||
The renderer can be constructed with a [classname]#DateTimeFormatter#, or with a custom pattern string. | |||
The locale is either given explicitly with the pattern, resolved from the given [classname]#DateTimeFormatter# or from the grid the renderer is attached to, if neither of the previous are given. | |||
For the pattern string syntax, refer to the following documentation: link:https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns[docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns]. | |||
Note we should use `SerializableProvider<DateTimeFormatter>` or lambda in the first case, because | |||
`DateTimeFormatter` is not serializable, and that may lead to problems in certain cases, for instance in a cluster environment. | |||
+ | |||
[source, java] | |||
---- | |||
DateTimeFormatter formatter = DateTimeFormatter | |||
LocalDateRenderer renderer = new LocalDateRenderer(() -> DateTimeFormatter | |||
.ofLocalizedDate(FormatStyle.LONG) | |||
.withLocale(Locale.ENGLISH); | |||
.withLocale(Locale.ENGLISH)); | |||
Column<Person, LocalDate> bornColumn = | |||
grid.addColumn( | |||
Person::getBirthDate, | |||
new LocalDateRenderer(formatter)); | |||
renderer); | |||
// Alternatively, with a custom pattern: | |||
Column<Person, LocalDate> bornColumn = | |||
@@ -559,14 +560,14 @@ Otherwise the same as [classname]#LocalDateRenderer#, except for the [classname] | |||
+ | |||
[source, java] | |||
---- | |||
DateTimeFormatter formatter = DateTimeFormatter | |||
.ofLocalizedDate(FormatStyle.LONG, FormatStyle.SHORT) | |||
.withLocale(Locale.ENGLISH); | |||
LocalDateTimeRenderer renderer = new LocalDateTimeRenderer( | |||
() -> DateTimeFormatter | |||
.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT) | |||
.withLocale(Locale.ENGLISH)); | |||
Column<Person, LocalDateTime> bornColumn = | |||
grid.addColumn( | |||
Person::getBirthDateAndTime, | |||
new LocalDateTimeRenderer(formatter)); | |||
grid.addColumn(Person::getBirthDateAndTime, renderer); | |||
// Alternatively, with a custom pattern: | |||
Column<Person, LocalDateTime> bornColumn = |
@@ -56,7 +56,7 @@ an [classname]#TreeDataProvider# with [classname]#TreeData# is used. If at any t | |||
---- | |||
TreeDataProvider<Project> dataProvider = (TreeDataProvider<Project>) treeGrid.getDataProvider(); | |||
TreeData<Project> data = dataProvider.getData(); | |||
TreeData<Project> data = dataProvider.getTreeData(); | |||
// add new items | |||
data.addItem(null, newProject); | |||
data.addItems(newProject, newProject.getChildren()); |
@@ -59,6 +59,11 @@ The [methodname]#setRows()# method sets the height of the component by the | |||
number of visible items in the selection boxes. Setting the height with | |||
[methodname]#setHeight()# to a defined value overrides the rows setting. | |||
[WARNING] | |||
The [classname]#TwinColSelect# does not provide lazy loading mechanism. | |||
Hence it will slow down significantly if used with large itemsets. | |||
The lazy loading feature could be implemented using two single column Grids instead. | |||
Common selection component features are described in | |||
<<dummy/../../../framework/components/components-selection#components.selection,"Selection | |||
Components">>. |
@@ -23,7 +23,7 @@ The basic tasks of a connector is to hook up to the widget and handle events | |||
from user interaction and changes received from the server. A connector also has | |||
a number of routine infrastructure methods which need to be implemented. | |||
[source,java] | |||
---- | |||
@Connect(MyComponent.class) | |||
public class MyComponentConnector |
@@ -34,7 +34,7 @@ extended component or UI as a parameter and passes it to __super.extend()__. | |||
For example, let us have a trivial example with an extension that takes no | |||
special parameters, and illustrates the three alternative APIs: | |||
[source,java] | |||
---- | |||
public class CapsLockWarning extends AbstractExtension { | |||
// You could pass it in the constructor | |||
@@ -56,7 +56,7 @@ public class CapsLockWarning extends AbstractExtension { | |||
The extension could then be added to a component as follows: | |||
[source,java] | |||
---- | |||
PasswordField password = new PasswordField("Give it"); | |||
@@ -96,7 +96,7 @@ In the following example, we implement a "Caps Lock warning" extension. It | |||
listens for changes in Caps Lock state and displays a floating warning element | |||
over the extended component if the Caps Lock is on. | |||
[source,java] | |||
---- | |||
@Connect(CapsLockWarning.class) | |||
public class CapsLockWarningConnector |
@@ -28,7 +28,7 @@ interface simply defines any methods that can be called through the interface. | |||
For example: | |||
[source,java] | |||
---- | |||
public interface MyComponentServerRpc extends ServerRpc { | |||
public void clicked(String buttonName); | |||
@@ -54,7 +54,7 @@ Before making a call, you need to instantiate the server RPC object with | |||
[methodname]#RpcProxy.create()#. This is usually done transparently by using [methodname]#getRpcProxy()#. After that, you can make calls through the | |||
server RPC interface that you defined, for example as follows: | |||
[source,java] | |||
---- | |||
@Connect(MyComponent.class) | |||
public class MyComponentConnector | |||
@@ -87,7 +87,7 @@ RPC calls are handled in a server-side implementation of the server RPC | |||
interface. The call and its parameters are serialized and passed to the server | |||
in an RPC request transparently. | |||
[source,java] | |||
---- | |||
public class MyComponent extends AbstractComponent { | |||
private MyComponentServerRpc rpc = |
@@ -19,7 +19,7 @@ The component state is usually managed by a __shared state__, described later in | |||
<<dummy/../../../framework/gwt/gwt-shared-state#gwt.shared-state,"Shared | |||
State">>. | |||
[source, java] | |||
---- | |||
public class MyComponent extends AbstractComponent { | |||
public MyComponent() { |
@@ -16,7 +16,7 @@ A shared state object simply needs to extend the | |||
[classname]#AbstractComponentState#. The member variables should normally be | |||
declared as public. | |||
[source, java] | |||
---- | |||
public class MyComponentState extends AbstractComponentState { | |||
public String text; | |||
@@ -42,7 +42,7 @@ and other classes needed by shared-state or RPC communication. | |||
For example, you could have the following definitions in the | |||
[filename]#.gwt.xml# descriptor: | |||
[source, xml] | |||
---- | |||
<source path="client" /> | |||
<source path="shared" /> | |||
@@ -59,7 +59,7 @@ A server-side component can access the shared state with the | |||
implementation with one that returns the shared state object cast to the proper | |||
type, as follows: | |||
[source, java] | |||
---- | |||
@Override | |||
public MyComponentState getState() { | |||
@@ -70,7 +70,7 @@ public MyComponentState getState() { | |||
You can then use the [methodname]#getState()# to access the shared state object | |||
with the proper type. | |||
[source, java] | |||
---- | |||
public MyComponent() { | |||
getState().setText("This is the initial state"); | |||
@@ -86,7 +86,7 @@ A connector can access a shared state with the [methodname]#getState()# method. | |||
The access should be read-only. It is required that you override the base | |||
implementation with one that returns the proper shared state type, as follows: | |||
[source, java] | |||
---- | |||
@Override | |||
public MyComponentState getState() { | |||
@@ -99,7 +99,7 @@ client-side. When a state change occurs, the [methodname]#onStateChanged()# | |||
method in the connector is called. You should always call the superclass | |||
method before anything else to handle changes to common component properties. | |||
[source, java] | |||
---- | |||
@Override | |||
public void onStateChanged(StateChangeEvent stateChangeEvent) { | |||
@@ -141,7 +141,7 @@ separately in arbitrary order. | |||
We can replace the [methodname]#onStateChange()# method in the earlier connector | |||
example with the following: | |||
[source, java] | |||
---- | |||
@OnStateChange("text") | |||
void updateText() { | |||
@@ -164,7 +164,7 @@ defines automatic delegation of the property value to the corresponding widget | |||
property of the same name and type, by calling the respective setter for the | |||
property in the widget. | |||
[source, java] | |||
---- | |||
public class MyComponentState extends AbstractComponentState { | |||
@DelegateToWidget | |||
@@ -178,7 +178,7 @@ example in <<gwt.shared-state.onstatechange>>. | |||
If you want to delegate a shared state property to a widget property of another | |||
name, you can give the property name as a string parameter for the annotation. | |||
[source, java] | |||
---- | |||
public class MyComponentState extends AbstractComponentState { | |||
@DelegateToWidget("description") | |||
@@ -196,7 +196,7 @@ only refer to a server-side component, while on the client-side you only have | |||
widgets. References to components can be made by referring to their connectors | |||
(all server-side components implement the [interfacename]#Connector# interface). | |||
[source, java] | |||
---- | |||
public class MyComponentState extends AbstractComponentState { | |||
public Connector otherComponent; | |||
@@ -205,7 +205,7 @@ public class MyComponentState extends AbstractComponentState { | |||
You could then access the component on the server-side as follows: | |||
[source, java] | |||
---- | |||
public class MyComponent { | |||
public void MyComponent(Component otherComponent) { | |||
@@ -241,7 +241,7 @@ serialized to the client-side separately. | |||
Let us begin with the server-side API: | |||
[source, java] | |||
---- | |||
public class MyComponent extends AbstractComponent { | |||
... | |||
@@ -259,7 +259,7 @@ public class MyComponent extends AbstractComponent { | |||
On the client-side, you can then get the URL of the resource with | |||
[methodname]#getResourceUrl()#. | |||
[source, java] | |||
---- | |||
@Override | |||
public void onStateChanged(StateChangeEvent stateChangeEvent) { | |||
@@ -273,7 +273,7 @@ public void onStateChanged(StateChangeEvent stateChangeEvent) { | |||
The widget could then use the URL, for example, as follows: | |||
[source, java] | |||
---- | |||
public class MyWidget extends Label { | |||
... |
@@ -22,7 +22,7 @@ sub-elements as it desires. | |||
For example, you could style a composite widget with an overall style and with | |||
separate styles for the sub-widgets as follows: | |||
[source, java] | |||
---- | |||
public class MyPickerWidget extends ComplexPanel { | |||
public static final String CLASSNAME = "mypicker"; |
@@ -77,20 +77,20 @@ link:https://github.com/elmot/liferay-7-solid-portlet-example/[] | |||
== Deployment a Portlet With OSGi (Maven) | |||
An OSGi portlet should be packaged as a JAR with a proper OSGi bundle | |||
manifest, and deployed to a portal that has its required bundles installed. | |||
The maven archetype `com.vaadin:vaadin-archetype-liferay-portlet:8.1.0` is a good starting point to build an OSGi portlet application. | |||
The maven archetype `com.vaadin:vaadin-archetype-liferay-portlet` is a good starting point to build an OSGi portlet application. | |||
The required bundles (and the application as well) can be installed using link:https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/blade-cli[blade client]. | |||
The latest client binary can be downloaded from the link: link:https://releases.liferay.com/tools/blade-cli/latest/blade.jar[] | |||
Here is an example script for doing that: | |||
Here is an example script for doing that (be sure to check the versions required by your project using *mvn dependency:list* ): | |||
[source, shell] | |||
---- | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/org/jsoup/jsoup/1.11.0/jsoup-1.11.0.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/org/jsoup/jsoup/1.11.2/jsoup-1.11.2.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/external/gentyref/1.2.0.vaadin1/gentyref-1.2.0.vaadin1.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-shared/8.3.2/vaadin-shared-8.3.2.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-server/8.3.2/vaadin-server-8.3.2.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-osgi-integration/8.3.2/vaadin-osgi-integration-8.3.2.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-client-compiled/8.3.2/vaadin-client-compiled-8.3.2.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-themes/8.3.2/vaadin-themes-8.3.2.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-liferay-integration/8.3.2/vaadin-liferay-integration-8.3.2.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-shared/8.6.3/vaadin-shared-8.6.3.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-server/8.6.3/vaadin-server-8.6.3.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-osgi-integration/8.6.3/vaadin-osgi-integration-8.6.3.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-client-compiled/8.6.3/vaadin-client-compiled-8.6.3.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-themes/8.6.3/vaadin-themes-8.6.3.jar | |||
java -jar blade.jar sh start https://repo1.maven.org/maven2/com/vaadin/vaadin-liferay-integration/8.6.3/vaadin-liferay-integration-8.6.3.jar | |||
java -jar blade.jar sh start file:<path_to_liferay_portlet.jar> | |||
---- |
@@ -10,7 +10,7 @@ layout: page | |||
A portlet UI is just like in a regular Vaadin application, a class that extends | |||
[classname]#com.vaadin.ui.UI#. | |||
[source, java] | |||
---- | |||
@Theme("myportlet") | |||
public class MyportletUI extends UI { | |||
@@ -72,6 +72,7 @@ the Project">>. | |||
Otherwise, the following snippet can be used. | |||
[source, java] | |||
---- | |||
@WebServlet(value = "/*", asyncSupported = true) | |||
@VaadinServletConfiguration(productionMode = false, |
@@ -900,7 +900,7 @@ delete.addClickListener(e -> this.delete()); | |||
---- | |||
TIP: For a truly re-usable form component in a real life project, you'd want to | |||
introduce an interface to replace the myUI field or, event better, use an event | |||
introduce an interface to replace the myUI field or, even better, use an event | |||
system like https://vaadin.com/wiki/-/wiki/main/Events+and+contexts[CDI events] | |||
to completely decouple the components. We'll leave that out of this tutorial for | |||
simplicity. |
@@ -9,7 +9,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<properties> |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.vaadin</groupId> | |||
<artifactId>vaadin-root</artifactId> | |||
<version>8.5-SNAPSHOT</version> | |||
<version>8.8-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>vaadin-liferay</artifactId> | |||
<name>vaadin-liferay</name> |