You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractColorPicker.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.io.Serializable;
  18. import java.util.Collection;
  19. import java.util.Locale;
  20. import java.util.Objects;
  21. import org.jsoup.nodes.Attributes;
  22. import org.jsoup.nodes.Element;
  23. import com.vaadin.shared.ui.colorpicker.AbstractColorPickerState;
  24. import com.vaadin.shared.ui.colorpicker.Color;
  25. import com.vaadin.shared.ui.colorpicker.ColorPickerServerRpc;
  26. import com.vaadin.ui.components.colorpicker.ColorPickerPopup;
  27. import com.vaadin.ui.declarative.DesignAttributeHandler;
  28. import com.vaadin.ui.declarative.DesignContext;
  29. /**
  30. * An abstract class that defines default implementation for a color picker
  31. * component.
  32. *
  33. * @since 7.0.0
  34. */
  35. public abstract class AbstractColorPicker extends AbstractField<Color> {
  36. /**
  37. * Interface for converting 2d-coordinates to a Color.
  38. */
  39. public interface Coordinates2Color extends Serializable {
  40. /**
  41. * Calculates a color from coordinates.
  42. *
  43. * @param x
  44. * the x-coordinate
  45. * @param y
  46. * the y-coordinate
  47. *
  48. * @return the color
  49. */
  50. public Color calculate(int x, int y);
  51. /**
  52. * Calculates coordinates from a color.
  53. *
  54. * @param c
  55. * the c
  56. *
  57. * @return the integer array with the coordinates
  58. */
  59. public int[] calculate(Color c);
  60. }
  61. /**
  62. * The style of the color picker popup.
  63. */
  64. public enum PopupStyle {
  65. /** A full popup with all tabs visible. */
  66. POPUP_NORMAL("normal"),
  67. /** A simple popup with only the swatches (palette) tab. */
  68. POPUP_SIMPLE("simple");
  69. private final String style;
  70. PopupStyle(String styleName) {
  71. style = styleName;
  72. }
  73. @Override
  74. public String toString() {
  75. return style;
  76. }
  77. }
  78. protected static final String STYLENAME_DEFAULT = "v-colorpicker";
  79. protected static final String STYLENAME_BUTTON = "v-button";
  80. protected static final String STYLENAME_AREA = "v-colorpicker-area";
  81. protected PopupStyle popupStyle = PopupStyle.POPUP_NORMAL;
  82. private ColorPickerPopup window;
  83. /** The currently selected color. */
  84. protected Color color;
  85. private UI parent;
  86. private String popupCaption = null;
  87. private int positionX = 0;
  88. private int positionY = 0;
  89. protected boolean rgbVisible = true;
  90. protected boolean hsvVisible = true;
  91. protected boolean swatchesVisible = true;
  92. protected boolean historyVisible = true;
  93. protected boolean textfieldVisible = true;
  94. private boolean modal;
  95. private ColorPickerServerRpc rpc = new ColorPickerServerRpc() {
  96. @Override
  97. public void openPopup(boolean openPopup) {
  98. showPopup(openPopup);
  99. }
  100. @Override
  101. public void changeColor(String col) {
  102. Color valueC = new Color(
  103. Integer.parseInt(col.substring(1, col.length()), 16));
  104. color = valueC;
  105. setValue(color, true);
  106. }
  107. };
  108. /**
  109. * Instantiates a new color picker.
  110. */
  111. public AbstractColorPicker() {
  112. this("Colors", Color.WHITE);
  113. }
  114. /**
  115. * Instantiates a new color picker.
  116. *
  117. * @param popupCaption
  118. * the caption of the popup window
  119. */
  120. public AbstractColorPicker(String popupCaption) {
  121. this(popupCaption, Color.WHITE);
  122. }
  123. /**
  124. * Instantiates a new color picker.
  125. *
  126. * @param popupCaption
  127. * the caption of the popup window
  128. * @param initialColor
  129. * the initial color
  130. */
  131. public AbstractColorPicker(String popupCaption, Color initialColor) {
  132. super();
  133. registerRpc(rpc);
  134. setValue(initialColor);
  135. this.popupCaption = popupCaption;
  136. setDefaultStyles();
  137. setCaption("");
  138. }
  139. /**
  140. * Returns the current selected color of this color picker.
  141. *
  142. * @return the selected color, not null
  143. */
  144. @Override
  145. public Color getValue() {
  146. return color;
  147. }
  148. /**
  149. * Sets the selected color of this color picker. If the new color is not
  150. * equal to getValue(), fires a {@link ValueChangeEvent}.
  151. *
  152. * @param color
  153. * the new selected color, not null
  154. * @throws NullPointerException
  155. * if {@code color} is {@code null}
  156. */
  157. @Override
  158. public void setValue(Color color) {
  159. Objects.requireNonNull(color, "color cannot be null");
  160. super.setValue(color);
  161. if (window != null) {
  162. window.setValue(color);
  163. }
  164. }
  165. /**
  166. * Set true if the component should show a default caption (css-code for the
  167. * currently selected color, e.g. #ffffff) when no other caption is
  168. * available.
  169. *
  170. * @param enabled
  171. * {@code true} to enable the default caption, {@code false} to
  172. * disable
  173. */
  174. public void setDefaultCaptionEnabled(boolean enabled) {
  175. getState().showDefaultCaption = enabled;
  176. }
  177. /**
  178. * Returns true if the component shows the default caption (css-code for the
  179. * currently selected color, e.g. #ffffff) if no other caption is available.
  180. *
  181. * @return {@code true} if the default caption is enabled, {@code false}
  182. * otherwise
  183. */
  184. public boolean isDefaultCaptionEnabled() {
  185. return getState(false).showDefaultCaption;
  186. }
  187. /**
  188. * Sets the position of the popup window.
  189. *
  190. * @param x
  191. * the x-coordinate
  192. * @param y
  193. * the y-coordinate
  194. */
  195. public void setPosition(int x, int y) {
  196. positionX = x;
  197. positionY = y;
  198. if (window != null) {
  199. window.setPositionX(x);
  200. window.setPositionY(y);
  201. }
  202. }
  203. /**
  204. * Sets the style of the popup window.
  205. *
  206. * @param style
  207. * the popup window style
  208. */
  209. public void setPopupStyle(PopupStyle style) {
  210. popupStyle = style;
  211. switch (style) {
  212. case POPUP_NORMAL:
  213. setRGBVisibility(true);
  214. setHSVVisibility(true);
  215. setSwatchesVisibility(true);
  216. setHistoryVisibility(true);
  217. setTextfieldVisibility(true);
  218. break;
  219. case POPUP_SIMPLE:
  220. setRGBVisibility(false);
  221. setHSVVisibility(false);
  222. setSwatchesVisibility(true);
  223. setHistoryVisibility(false);
  224. setTextfieldVisibility(false);
  225. break;
  226. default:
  227. assert false : "Unknown popup style " + style;
  228. }
  229. }
  230. /**
  231. * Gets the style for the popup window.
  232. *
  233. * @since 7.5.0
  234. * @return popup window style
  235. */
  236. public PopupStyle getPopupStyle() {
  237. return popupStyle;
  238. }
  239. /**
  240. * Sets the visibility of the RGB tab.
  241. *
  242. * @param visible
  243. * {@code true} to display the RGB tab, {@code false} to hide it
  244. */
  245. public void setRGBVisibility(boolean visible) {
  246. if (!visible && !hsvVisible && !swatchesVisible) {
  247. throw new IllegalArgumentException("Cannot hide all tabs.");
  248. }
  249. rgbVisible = visible;
  250. if (window != null) {
  251. window.setRGBTabVisible(visible);
  252. }
  253. }
  254. /**
  255. * Gets the visibility of the RGB Tab.
  256. *
  257. * @since 7.5.0
  258. * @return visibility of the RGB tab
  259. */
  260. public boolean getRGBVisibility() {
  261. return rgbVisible;
  262. }
  263. /**
  264. * Sets the visibility of the HSV Tab.
  265. *
  266. * @param visible
  267. * {@code true} to display the HSV tab, {@code false} to hide it
  268. */
  269. public void setHSVVisibility(boolean visible) {
  270. if (!visible && !rgbVisible && !swatchesVisible) {
  271. throw new IllegalArgumentException("Cannot hide all tabs.");
  272. }
  273. hsvVisible = visible;
  274. if (window != null) {
  275. window.setHSVTabVisible(visible);
  276. }
  277. }
  278. /**
  279. * Gets the visibility of the HSV tab.
  280. *
  281. * @since 7.5.0
  282. * @return {@code true} if the HSV tab is currently displayed, {@code false}
  283. * otherwise
  284. */
  285. public boolean getHSVVisibility() {
  286. return hsvVisible;
  287. }
  288. /**
  289. * Sets the visibility of the Swatches (palette) tab.
  290. *
  291. * @param visible
  292. * {@code true} to display the Swatches tab, {@code false} to
  293. * hide it
  294. */
  295. public void setSwatchesVisibility(boolean visible) {
  296. if (!visible && !hsvVisible && !rgbVisible) {
  297. throw new IllegalArgumentException("Cannot hide all tabs.");
  298. }
  299. swatchesVisible = visible;
  300. if (window != null) {
  301. window.setSwatchesTabVisible(visible);
  302. }
  303. }
  304. /**
  305. * Gets the visibility of the Swatches (palette) tab.
  306. *
  307. * @since 7.5.0
  308. * @return {@code true} if the Swatches tab is currently displayed,
  309. * {@code false} otherwise
  310. */
  311. public boolean getSwatchesVisibility() {
  312. return swatchesVisible;
  313. }
  314. /**
  315. * Sets the visibility of the color history, displaying recently picked
  316. * colors.
  317. *
  318. * @param visible
  319. * {@code true} to display the history, {@code false} to hide it
  320. */
  321. public void setHistoryVisibility(boolean visible) {
  322. historyVisible = visible;
  323. if (window != null) {
  324. window.setHistoryVisible(visible);
  325. }
  326. }
  327. /**
  328. * Gets the visibility of the Color history.
  329. *
  330. * @since 7.5.0
  331. * @return {@code true} if the history is currently displayed, {@code false}
  332. * otherwise
  333. */
  334. public boolean getHistoryVisibility() {
  335. return historyVisible;
  336. }
  337. /**
  338. * Sets the visibility of the CSS color code text field.
  339. *
  340. * @param visible
  341. * {@code true} to display the CSS text field, {@code false} to
  342. * hide it
  343. */
  344. public void setTextfieldVisibility(boolean visible) {
  345. textfieldVisible = visible;
  346. if (window != null) {
  347. window.setPreviewVisible(visible);
  348. }
  349. }
  350. /**
  351. * Gets the visibility of CSS color code text field.
  352. *
  353. * @since 7.5.0
  354. * @return {@code true} if the CSS text field is currently displayed,
  355. * {@code false} otherwise
  356. */
  357. public boolean getTextfieldVisibility() {
  358. return textfieldVisible;
  359. }
  360. @Override
  361. protected AbstractColorPickerState getState() {
  362. return (AbstractColorPickerState) super.getState();
  363. }
  364. @Override
  365. protected AbstractColorPickerState getState(boolean markAsDirty) {
  366. return (AbstractColorPickerState) super.getState(markAsDirty);
  367. }
  368. /**
  369. * Sets the default styles of the component.
  370. */
  371. protected abstract void setDefaultStyles();
  372. /**
  373. * Shows a popup-window for color selection.
  374. */
  375. public void showPopup() {
  376. showPopup(true);
  377. }
  378. /**
  379. * Hides a popup-window for color selection.
  380. */
  381. public void hidePopup() {
  382. showPopup(false);
  383. }
  384. /**
  385. * Shows or hides the popup window depending on the given parameter. If
  386. * there is no such window yet, one is created.
  387. *
  388. * @param open
  389. * {@code true} to display the popup, {@code false} to hide it
  390. */
  391. protected void showPopup(boolean open) {
  392. if (open && !isReadOnly()) {
  393. if (parent == null) {
  394. parent = getUI();
  395. }
  396. Color color = getValue();
  397. if (window == null) {
  398. window = new ColorPickerPopup(color);
  399. window.setCaption(popupCaption);
  400. window.setRGBTabVisible(rgbVisible);
  401. window.setHSVTabVisible(hsvVisible);
  402. window.setSwatchesTabVisible(swatchesVisible);
  403. window.setHistoryVisible(historyVisible);
  404. window.setPreviewVisible(textfieldVisible);
  405. window.addCloseListener(
  406. event -> getState().popupVisible = false);
  407. window.addValueChangeListener(event -> {
  408. window.setValue(color);
  409. setValue(event.getValue(), true);
  410. });
  411. window.getHistory().setValue(color);
  412. window.setPositionX(positionX);
  413. window.setPositionY(positionY);
  414. window.setVisible(true);
  415. window.setValue(color);
  416. window.setModal(modal);
  417. parent.addWindow(window);
  418. window.focus();
  419. } else if (!parent.equals(window.getParent())) {
  420. window.setRGBTabVisible(rgbVisible);
  421. window.setHSVTabVisible(hsvVisible);
  422. window.setSwatchesTabVisible(swatchesVisible);
  423. window.setHistoryVisible(historyVisible);
  424. window.setPreviewVisible(textfieldVisible);
  425. window.setValue(color);
  426. window.getHistory().setValue(color);
  427. window.setVisible(true);
  428. window.setModal(modal);
  429. parent.addWindow(window);
  430. window.focus();
  431. }
  432. window.setValue(color);
  433. } else if (window != null) {
  434. window.setVisible(false);
  435. parent.removeWindow(window);
  436. }
  437. getState().popupVisible = open;
  438. }
  439. @Override
  440. public void readDesign(Element design, DesignContext designContext) {
  441. super.readDesign(design, designContext);
  442. Attributes attributes = design.attributes();
  443. if (design.hasAttr("color")) {
  444. // Ignore the # character
  445. String hexColor = DesignAttributeHandler
  446. .readAttribute("color", attributes, String.class)
  447. .substring(1);
  448. doSetValue(new Color(Integer.parseInt(hexColor, 16)));
  449. }
  450. if (design.hasAttr("popup-style")) {
  451. setPopupStyle(PopupStyle.valueOf("POPUP_"
  452. + attributes.get("popup-style").toUpperCase(Locale.ROOT)));
  453. }
  454. if (design.hasAttr("position")) {
  455. String[] position = attributes.get("position").split(",");
  456. setPosition(Integer.parseInt(position[0]),
  457. Integer.parseInt(position[1]));
  458. }
  459. }
  460. @Override
  461. public void writeDesign(Element design, DesignContext designContext) {
  462. super.writeDesign(design, designContext);
  463. Attributes attribute = design.attributes();
  464. DesignAttributeHandler.writeAttribute("color", attribute,
  465. getValue().getCSS(), Color.WHITE.getCSS(), String.class,
  466. designContext);
  467. DesignAttributeHandler.writeAttribute("popup-style", attribute,
  468. popupStyle == PopupStyle.POPUP_NORMAL ? "normal" : "simple",
  469. "normal", String.class, designContext);
  470. DesignAttributeHandler.writeAttribute("position", attribute,
  471. positionX + "," + positionY, "0,0", String.class,
  472. designContext);
  473. }
  474. @Override
  475. protected Collection<String> getCustomAttributes() {
  476. Collection<String> result = super.getCustomAttributes();
  477. result.add("color");
  478. result.add("position");
  479. result.add("popup-style");
  480. return result;
  481. }
  482. @Override
  483. protected void doSetValue(Color color) {
  484. this.color = color;
  485. getState().color = color.getCSS();
  486. }
  487. @Override
  488. public Color getEmptyValue() {
  489. return Color.WHITE;
  490. }
  491. /**
  492. * Sets ColorPicker modality. When a modal ColorPicker is open, components
  493. * outside that ColorPicker cannot be accessed.
  494. * <p>
  495. * Note: It must be set to {@code true} if ColorPicker is a child of modal
  496. * {@link Window}
  497. * </p>
  498. *
  499. * @see Window#setModal
  500. * @since 8.4.1
  501. * @param modal
  502. * true if modality is to be turned on
  503. */
  504. public void setModal(boolean modal) {
  505. this.modal = modal;
  506. }
  507. /**
  508. * Checks the modality of the dialog.
  509. *
  510. * @see #setModal(boolean)
  511. * @since 8.4.1
  512. * @return true if the dialog is modal, false otherwise
  513. */
  514. public boolean isModal() {
  515. return this.modal;
  516. }
  517. }