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.

VDebugWindow.java 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134
  1. /*
  2. * Copyright 2000-2016 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.client.debug.internal;
  17. import java.util.ArrayList;
  18. import java.util.Date;
  19. import java.util.List;
  20. import com.google.gwt.core.client.Duration;
  21. import com.google.gwt.core.client.Scheduler;
  22. import com.google.gwt.core.shared.GWT;
  23. import com.google.gwt.dom.client.Element;
  24. import com.google.gwt.dom.client.NativeEvent;
  25. import com.google.gwt.dom.client.Style;
  26. import com.google.gwt.dom.client.Style.Cursor;
  27. import com.google.gwt.dom.client.Style.Overflow;
  28. import com.google.gwt.event.dom.client.ClickEvent;
  29. import com.google.gwt.event.dom.client.ClickHandler;
  30. import com.google.gwt.event.dom.client.MouseDownEvent;
  31. import com.google.gwt.event.dom.client.MouseDownHandler;
  32. import com.google.gwt.event.dom.client.MouseEvent;
  33. import com.google.gwt.event.dom.client.MouseMoveEvent;
  34. import com.google.gwt.event.dom.client.MouseMoveHandler;
  35. import com.google.gwt.event.logical.shared.ResizeEvent;
  36. import com.google.gwt.event.shared.HandlerRegistration;
  37. import com.google.gwt.http.client.UrlBuilder;
  38. import com.google.gwt.i18n.client.DateTimeFormat;
  39. import com.google.gwt.i18n.client.NumberFormat;
  40. import com.google.gwt.storage.client.Storage;
  41. import com.google.gwt.user.client.DOM;
  42. import com.google.gwt.user.client.Event;
  43. import com.google.gwt.user.client.Event.NativePreviewEvent;
  44. import com.google.gwt.user.client.Event.NativePreviewHandler;
  45. import com.google.gwt.user.client.Timer;
  46. import com.google.gwt.user.client.Window;
  47. import com.google.gwt.user.client.Window.Location;
  48. import com.google.gwt.user.client.ui.Button;
  49. import com.google.gwt.user.client.ui.FlowPanel;
  50. import com.google.gwt.user.client.ui.RootPanel;
  51. import com.google.gwt.user.client.ui.SimplePanel;
  52. import com.google.gwt.user.client.ui.Widget;
  53. import com.vaadin.client.ApplicationConnection;
  54. import com.vaadin.client.ValueMap;
  55. import com.vaadin.client.ui.VOverlay;
  56. /**
  57. * Debug window implementation.
  58. *
  59. * @since 7.1
  60. * @author Vaadin Ltd
  61. */
  62. public final class VDebugWindow extends VOverlay {
  63. // CSS classes
  64. static final String STYLENAME = "v-debugwindow";
  65. static final String STYLENAME_BUTTON = STYLENAME + "-button";
  66. static final String STYLENAME_ACTIVE = "active";
  67. protected static final String STYLENAME_HEAD = STYLENAME + "-head";
  68. protected static final String STYLENAME_TABS = STYLENAME + "-tabs";
  69. protected static final String STYLENAME_TAB = STYLENAME + "-tab";
  70. protected static final String STYLENAME_CONTROLS = STYLENAME + "-controls";
  71. protected static final String STYLENAME_SECTION_HEAD = STYLENAME
  72. + "-section-head";
  73. protected static final String STYLENAME_CONTENT = STYLENAME + "-content";
  74. protected static final String STYLENAME_SELECTED = "selected";
  75. // drag this far before actually moving window
  76. protected static final int MOVE_TRESHOLD = 5;
  77. // window minimum height, minimum width comes from tab+controls
  78. protected static final int MIN_HEIGHT = 40;
  79. // size of area to grab for resize; bottom corners size in CSS
  80. protected static final int HANDLE_SIZE = 7;
  81. // identifiers for localStorage
  82. private static final String STORAGE_PREFIX = "v-debug-";
  83. private static final String STORAGE_FULL_X = "x";
  84. private static final String STORAGE_FULL_Y = "y";
  85. private static final String STORAGE_FULL_W = "w";
  86. private static final String STORAGE_FULL_H = "h";
  87. private static final String STORAGE_MIN_X = "mx";
  88. private static final String STORAGE_MIN_Y = "my";
  89. private static final String STORAGE_ACTIVE_SECTION = "t";
  90. private static final String STORAGE_IS_MINIMIZED = "m";
  91. private static final String STORAGE_FONT_SIZE = "s";
  92. // state, these are persisted
  93. protected Section activeSection;
  94. protected boolean minimized = false;
  95. protected int fullX = -10;
  96. protected int fullY = -10;
  97. protected int fullW = 300;
  98. protected int fullH = 150;
  99. protected int minX = -10;
  100. protected int minY = 10;
  101. protected int fontSize = 1; // 0-2
  102. // Timers since application start, and last timer reset
  103. private static final Duration START = new Duration();
  104. private static Duration lastReset = START;
  105. // outer panel
  106. protected FlowPanel window = new FlowPanel();
  107. // top (tabs + controls)
  108. protected FlowPanel head = new FlowPanel();
  109. protected FlowPanel tabs = new FlowPanel();
  110. protected FlowPanel controls = new FlowPanel();
  111. protected Button minimize = new DebugButton(Icon.MINIMIZE, "Minimize");
  112. protected Button menu = new DebugButton(Icon.MENU, "Menu");
  113. protected Button close = new DebugButton(Icon.CLOSE, "Close");
  114. // menu
  115. protected Menu menuPopup = new Menu();
  116. // section specific area
  117. protected FlowPanel sectionHead = new FlowPanel();
  118. // content wrapper
  119. protected SimplePanel content = new SimplePanel();
  120. // sections
  121. protected List<Section> sections = new ArrayList<>();
  122. // handles resize/move
  123. protected HandlerRegistration mouseDownHandler = null;
  124. protected HandlerRegistration mouseMoveHandler = null;
  125. // TODO this class should really be a singleton.
  126. static VDebugWindow instance;
  127. /**
  128. * This class should only be instantiated by the framework, use
  129. * {@link #get()} instead to get the singleton instance.
  130. * <p>
  131. * {@link VDebugWindow} provides windowing functionality and shows
  132. * {@link Section}s added with {@link #addSection(Section)} as tabs.
  133. * </p>
  134. * <p>
  135. * {@link Section#getTabButton()} is called to obtain a unique id for the
  136. * Sections; the id should actually be an identifier for an icon in the
  137. * icon-font in use.
  138. * </p>
  139. * <p>
  140. * {@link Section#getControls()} and {@link Section#getContent()} is called
  141. * when the Section is activated (displayed). Additionally
  142. * {@link Section#show()} is called to allow the Section to initialize
  143. * itself as needed when shown. Conversely {@link Section#hide()} is called
  144. * when the Section is deactivated.
  145. * </p>
  146. * <p>
  147. * Sections should take care to prefix CSS classnames used with
  148. * {@link VDebugWindow}.{@link #STYLENAME} to avoid that application theme
  149. * interferes with the debug window content.
  150. * </p>
  151. * <p>
  152. * Some of the window state, such as position and size, is persisted to
  153. * localStorage. Sections can use
  154. * {@link #writeState(Storage, String, Object)} and
  155. * {@link #readState(Storage, String, String)} (and relatives) to write and
  156. * read own persisted settings, keys will automatically be prefixed with
  157. * {@value #STORAGE_PREFIX}.
  158. * </p>
  159. */
  160. public VDebugWindow() {
  161. super(false, false);
  162. getElement().getStyle().setOverflow(Overflow.HIDDEN);
  163. setStylePrimaryName(STYLENAME);
  164. setWidget(window);
  165. window.add(head);
  166. head.add(tabs);
  167. head.add(controls);
  168. head.add(sectionHead);
  169. window.add(content);
  170. addHandles();
  171. head.setStylePrimaryName(STYLENAME_HEAD);
  172. tabs.setStylePrimaryName(STYLENAME_TABS);
  173. controls.setStylePrimaryName(STYLENAME_CONTROLS);
  174. sectionHead.setStylePrimaryName(STYLENAME_SECTION_HEAD);
  175. content.setStylePrimaryName(STYLENAME_CONTENT);
  176. // add controls TODO move these
  177. controls.add(menu);
  178. menu.addClickHandler(new ClickHandler() {
  179. @Override
  180. public void onClick(ClickEvent event) {
  181. menuPopup.showRelativeTo(menu);
  182. }
  183. });
  184. controls.add(minimize);
  185. minimize.addClickHandler(new ClickHandler() {
  186. @Override
  187. public void onClick(ClickEvent event) {
  188. toggleMinimized();
  189. writeStoredState();
  190. }
  191. });
  192. controls.add(close);
  193. close.addClickHandler(new ClickHandler() {
  194. @Override
  195. public void onClick(ClickEvent event) {
  196. close();
  197. }
  198. });
  199. Style s = content.getElement().getStyle();
  200. s.setOverflow(Overflow.AUTO);
  201. // move/resize
  202. final MouseHandler mouseHandler = new MouseHandler();
  203. mouseDownHandler = this.addDomHandler(mouseHandler,
  204. MouseDownEvent.getType());
  205. mouseMoveHandler = this.addDomHandler(mouseHandler,
  206. MouseMoveEvent.getType());
  207. }
  208. /**
  209. * Adds dummy handle elements to the bottom corners that might have
  210. * scrollbars that interfere with resizing on some platforms.
  211. *
  212. * @since 7.1
  213. */
  214. private void addHandles() {
  215. Element el = DOM.createDiv();
  216. el.setClassName(VDebugWindow.STYLENAME + "-handle "
  217. + VDebugWindow.STYLENAME + "-handle-sw");
  218. content.getElement().appendChild(el);
  219. el = DOM.createDiv();
  220. el.setClassName(VDebugWindow.STYLENAME + "-handle "
  221. + VDebugWindow.STYLENAME + "-handle-se");
  222. content.getElement().appendChild(el);
  223. }
  224. /**
  225. * Gets the {@link #VDebugWindow()} singleton instance.
  226. *
  227. * @return
  228. */
  229. public static VDebugWindow get() {
  230. if (instance == null) {
  231. instance = GWT.create(VDebugWindow.class);
  232. }
  233. return instance;
  234. }
  235. /**
  236. * Closes the window and stops visual logging.
  237. */
  238. public void close() {
  239. // TODO disable even more
  240. if (mouseDownHandler != null) {
  241. mouseDownHandler.removeHandler();
  242. mouseMoveHandler.removeHandler();
  243. mouseDownHandler = null;
  244. mouseMoveHandler = null;
  245. }
  246. Highlight.hideAll();
  247. hide();
  248. }
  249. boolean isClosed() {
  250. return !isShowing();
  251. }
  252. /**
  253. * Reads the stored state from localStorage.
  254. */
  255. private void readStoredState() {
  256. Storage storage = Storage.getLocalStorageIfSupported();
  257. if (storage == null) {
  258. return;
  259. }
  260. fullX = readState(storage, STORAGE_FULL_X, -510);
  261. fullY = readState(storage, STORAGE_FULL_Y, -230);
  262. fullW = readState(storage, STORAGE_FULL_W, 500);
  263. fullH = readState(storage, STORAGE_FULL_H, 150);
  264. minX = readState(storage, STORAGE_MIN_X, -40);
  265. minY = readState(storage, STORAGE_MIN_Y, -70);
  266. setFontSize(readState(storage, STORAGE_FONT_SIZE, 1));
  267. activateSection(readState(storage, STORAGE_ACTIVE_SECTION, 0));
  268. setMinimized(readState(storage, STORAGE_IS_MINIMIZED, false));
  269. applyPositionAndSize();
  270. }
  271. /**
  272. * Writes the persistent state to localStorage.
  273. */
  274. private void writeStoredState() {
  275. if (isClosed()) {
  276. return;
  277. }
  278. Storage storage = Storage.getLocalStorageIfSupported();
  279. if (storage == null) {
  280. return;
  281. }
  282. writeState(storage, STORAGE_FULL_X, fullX);
  283. writeState(storage, STORAGE_FULL_Y, fullY);
  284. writeState(storage, STORAGE_FULL_W, fullW);
  285. writeState(storage, STORAGE_FULL_H, fullH);
  286. writeState(storage, STORAGE_MIN_X, minX);
  287. writeState(storage, STORAGE_MIN_Y, minY);
  288. writeState(storage, STORAGE_FONT_SIZE, fontSize);
  289. int activeIdx = getActiveSection();
  290. if (activeIdx >= 0) {
  291. writeState(storage, STORAGE_ACTIVE_SECTION, activeIdx);
  292. }
  293. writeState(storage, STORAGE_IS_MINIMIZED, minimized);
  294. }
  295. /**
  296. * Writes the given value to the given {@link Storage} using the given key
  297. * (automatically prefixed with {@value #STORAGE_PREFIX}).
  298. *
  299. * @param storage
  300. * @param key
  301. * @param value
  302. */
  303. static void writeState(Storage storage, String key, Object value) {
  304. storage.setItem(STORAGE_PREFIX + key, String.valueOf(value));
  305. }
  306. /**
  307. * Returns the item with the given key (automatically prefixed with
  308. * {@value #STORAGE_PREFIX}) as an int from the given {@link Storage},
  309. * returning the given default value instead if not successful (e.g missing
  310. * item).
  311. *
  312. * @param storage
  313. * @param key
  314. * @param def
  315. * @return stored or default value
  316. */
  317. static int readState(Storage storage, String key, int def) {
  318. try {
  319. return Integer.parseInt(storage.getItem(STORAGE_PREFIX + key));
  320. } catch (Exception e) {
  321. return def;
  322. }
  323. }
  324. /**
  325. * Returns the item with the given key (automatically prefixed with
  326. * {@value #STORAGE_PREFIX}) as a boolean from the given {@link Storage},
  327. * returning the given default value instead if not successful (e.g missing
  328. * item).
  329. *
  330. * @param storage
  331. * @param key
  332. * @param def
  333. * @return stored or default value
  334. */
  335. static boolean readState(Storage storage, String key, boolean def) {
  336. try {
  337. return Boolean.parseBoolean(storage.getItem(STORAGE_PREFIX + key));
  338. } catch (Exception e) {
  339. return def;
  340. }
  341. }
  342. /**
  343. * Returns the item with the given key (automatically prefixed with
  344. * {@value #STORAGE_PREFIX}) as a String from the given {@link Storage},
  345. * returning the given default value instead if not successful (e.g missing
  346. * item).
  347. *
  348. * @param storage
  349. * @param key
  350. * @param def
  351. * @return stored or default value
  352. */
  353. static String readState(Storage storage, String key, String def) {
  354. String val = storage.getItem(STORAGE_PREFIX + key);
  355. return val != null ? val : def;
  356. }
  357. /**
  358. * Resets (clears) the stored state from localStorage.
  359. */
  360. private void resetStoredState() {
  361. Storage storage = Storage.getLocalStorageIfSupported();
  362. if (storage == null) {
  363. return;
  364. }
  365. // note: length is live
  366. for (int i = 0; i < storage.getLength();) {
  367. String key = storage.key(i);
  368. if (key.startsWith(STORAGE_PREFIX)) {
  369. removeState(storage, key.substring(STORAGE_PREFIX.length()));
  370. } else {
  371. i++;
  372. }
  373. }
  374. }
  375. /**
  376. * Removes the item with the given key (automatically prefixed with
  377. * {@value #STORAGE_PREFIX}) from the given {@link Storage}.
  378. *
  379. * @param storage
  380. * @param key
  381. */
  382. private void removeState(Storage storage, String key) {
  383. storage.removeItem(STORAGE_PREFIX + key);
  384. }
  385. /**
  386. * Applies the appropriate instance variables for width, height, x, y
  387. * depending on if the window is minimized or not.
  388. *
  389. * If the value is negative, the window is positioned that amount of pixels
  390. * from the right/bottom instead of left/top.
  391. *
  392. * Finally, the position is bounds-checked so that the window is not moved
  393. * off-screen (the adjusted values are not saved).
  394. */
  395. private void applyPositionAndSize() {
  396. int x = 0;
  397. int y = 0;
  398. if (minimized) {
  399. x = minX;
  400. if (minX < 0) {
  401. x = Window.getClientWidth() + minX;
  402. }
  403. y = minY;
  404. if (minY < 0) {
  405. y = Window.getClientHeight() + minY;
  406. }
  407. } else {
  408. x = fullX;
  409. if (fullX < 0) {
  410. x = Window.getClientWidth() + fullX;
  411. }
  412. y = fullY;
  413. if (y < 0) {
  414. y = Window.getClientHeight() + fullY;
  415. }
  416. content.setWidth(fullW + "px");
  417. content.setHeight(fullH + "px");
  418. }
  419. applyBounds(x, y);
  420. }
  421. private void applyBounds() {
  422. int x = getPopupLeft();
  423. int y = getPopupTop();
  424. applyBounds(x, y);
  425. }
  426. private void applyBounds(int x, int y) {
  427. // bounds check
  428. if (x < 0) {
  429. x = 0;
  430. }
  431. if (x > Window.getClientWidth() - getOffsetWidth()) {
  432. // not allowed off-screen to the right
  433. x = Window.getClientWidth() - getOffsetWidth();
  434. }
  435. if (y > Window.getClientHeight() - getOffsetHeight()) {
  436. y = Window.getClientHeight() - getOffsetHeight();
  437. }
  438. if (y < 0) {
  439. y = 0;
  440. }
  441. setPopupPosition(x, y);
  442. }
  443. /**
  444. * Reads position and size from the DOM to local variables (which in turn
  445. * can be stored to localStorage)
  446. */
  447. private void readPositionAndSize() {
  448. int x = getPopupLeft();
  449. int fromRight = Window.getClientWidth() - x - getOffsetWidth();
  450. if (fromRight < x) {
  451. x -= Window.getClientWidth();
  452. }
  453. int y = getPopupTop();
  454. int fromBottom = Window.getClientHeight() - y - getOffsetHeight();
  455. if (fromBottom < y) {
  456. y -= Window.getClientHeight();
  457. }
  458. if (minimized) {
  459. minY = y;
  460. minX = x;
  461. } else {
  462. fullY = y;
  463. fullX = x;
  464. fullW = content.getOffsetWidth();
  465. fullH = content.getOffsetHeight();
  466. }
  467. }
  468. /**
  469. * Adds the given {@link Section} as a tab in the {@link VDebugWindow} UI.
  470. * {@link Section#getTabButton()} is called to obtain a button which is used
  471. * tab.
  472. *
  473. * @param section
  474. */
  475. public void addSection(final Section section) {
  476. Button b = section.getTabButton();
  477. b.addClickHandler(new ClickHandler() {
  478. @Override
  479. public void onClick(ClickEvent event) {
  480. activateSection(section);
  481. writeStoredState();
  482. }
  483. });
  484. b.setStylePrimaryName(STYLENAME_TAB);
  485. tabs.add(b);
  486. sections.add(section);
  487. if (activeSection == null) {
  488. activateSection(section);
  489. }
  490. }
  491. /**
  492. * Activates the given {@link Section}
  493. *
  494. * @param section
  495. */
  496. void activateSection(Section section) {
  497. if (section != null && section != activeSection) {
  498. Highlight.hideAll();
  499. // remove old stuff
  500. if (activeSection != null) {
  501. activeSection.hide();
  502. content.remove(activeSection.getContent());
  503. sectionHead.remove(activeSection.getControls());
  504. }
  505. // update tab styles
  506. for (int i = 0; i < tabs.getWidgetCount(); i++) {
  507. Widget tab = tabs.getWidget(i);
  508. tab.setStyleDependentName(STYLENAME_SELECTED,
  509. tab == section.getTabButton());
  510. }
  511. // add new stuff
  512. content.add(section.getContent());
  513. sectionHead.add(section.getControls());
  514. activeSection = section;
  515. activeSection.show();
  516. }
  517. }
  518. void activateSection(int n) {
  519. if (n < sections.size()) {
  520. activateSection(sections.get(n));
  521. }
  522. }
  523. int getActiveSection() {
  524. return sections.indexOf(activeSection);
  525. }
  526. /**
  527. * Toggles the window between minimized and full states.
  528. */
  529. private void toggleMinimized() {
  530. setMinimized(!minimized);
  531. writeStoredState();
  532. }
  533. /**
  534. * Sets whether or not the window is minimized.
  535. *
  536. * @param minimized
  537. */
  538. private void setMinimized(boolean minimized) {
  539. this.minimized = minimized;
  540. tabs.setVisible(!minimized);
  541. content.setVisible(!minimized);
  542. sectionHead.setVisible(!minimized);
  543. menu.setVisible(!minimized);
  544. applyPositionAndSize();
  545. }
  546. /**
  547. * Sets the font size in use.
  548. *
  549. * @param size
  550. */
  551. private void setFontSize(int size) {
  552. removeStyleDependentName("size" + fontSize);
  553. fontSize = size;
  554. addStyleDependentName("size" + size);
  555. }
  556. /**
  557. * Gets the font size currently in use.
  558. *
  559. * @return
  560. */
  561. private int getFontSize() {
  562. return fontSize;
  563. }
  564. /**
  565. * Gets the milliseconds since application start.
  566. *
  567. * @return
  568. */
  569. static int getMillisSinceStart() {
  570. return START.elapsedMillis();
  571. }
  572. /**
  573. * Gets the milliseconds since last {@link #resetTimer()} call.
  574. *
  575. * @return
  576. */
  577. static int getMillisSinceReset() {
  578. return lastReset.elapsedMillis();
  579. }
  580. /**
  581. * Resets the timer.
  582. *
  583. * @return Milliseconds elapsed since the timer was last reset.
  584. */
  585. static int resetTimer() {
  586. int sinceLast = lastReset.elapsedMillis();
  587. lastReset = new Duration();
  588. return sinceLast;
  589. }
  590. /**
  591. * Gets a nicely formatted string with timing information suitable for
  592. * display in tooltips.
  593. *
  594. * @param sinceStart
  595. * @param sinceReset
  596. * @return
  597. */
  598. static String getTimingTooltip(int sinceStart, int sinceReset) {
  599. String title = formatDuration(sinceStart) + " since start";
  600. title += ", &#10; " + formatDuration(sinceReset) + " since timer reset";
  601. title += " &#10; @ "
  602. + DateTimeFormat.getFormat("HH:mm:ss.SSS").format(new Date());
  603. return title;
  604. }
  605. /**
  606. * Formats the given milliseconds as hours, minutes, seconds and
  607. * milliseconds.
  608. *
  609. * @param ms
  610. * @return
  611. */
  612. static String formatDuration(int ms) {
  613. NumberFormat fmt = NumberFormat.getFormat("00");
  614. String seconds = fmt.format((ms / 1000) % 60);
  615. String minutes = fmt.format((ms / (1000 * 60)) % 60);
  616. String hours = fmt.format((ms / (1000 * 60 * 60)) % 24);
  617. String millis = NumberFormat.getFormat("000").format(ms % 1000);
  618. return hours + "h " + minutes + "m " + seconds + "s " + millis + "ms";
  619. }
  620. /**
  621. * Called when the window is initialized.
  622. */
  623. public void init() {
  624. show();
  625. /*
  626. * Finalize initialization when all entry points have had the chance to
  627. * e.g. register new sections.
  628. */
  629. Scheduler.get().scheduleFinally(() -> {
  630. readStoredState();
  631. Window.addResizeHandler(
  632. new com.google.gwt.event.logical.shared.ResizeHandler() {
  633. Timer t = new Timer() {
  634. @Override
  635. public void run() {
  636. applyPositionAndSize();
  637. }
  638. };
  639. @Override
  640. public void onResize(ResizeEvent event) {
  641. t.cancel();
  642. // TODO less
  643. t.schedule(1000);
  644. }
  645. });
  646. });
  647. }
  648. /**
  649. * Called when the result from analyzeLayouts is received.
  650. *
  651. * @param ac
  652. * @param meta
  653. */
  654. public void meta(ApplicationConnection ac, ValueMap meta) {
  655. if (isClosed()) {
  656. return;
  657. }
  658. for (Section s : sections) {
  659. s.meta(ac, meta);
  660. }
  661. }
  662. /**
  663. * Called when a response is received.
  664. *
  665. * @param ac
  666. * @param uidl
  667. */
  668. public void uidl(ApplicationConnection ac, ValueMap uidl) {
  669. if (isClosed()) {
  670. return;
  671. }
  672. for (Section s : sections) {
  673. s.uidl(ac, uidl);
  674. }
  675. }
  676. /**
  677. * Gets the container element for this window. The debug window is always
  678. * global to the document and not related to any
  679. * {@link ApplicationConnection} in particular.
  680. *
  681. * @return The global overlay container element.
  682. */
  683. @Override
  684. public com.google.gwt.user.client.Element getOverlayContainer() {
  685. return RootPanel.get().getElement();
  686. }
  687. /*
  688. * Inner classes
  689. */
  690. /**
  691. * Popup menu for {@link VDebugWindow}.
  692. *
  693. * @since 7.1
  694. * @author Vaadin Ltd
  695. */
  696. protected class Menu extends VOverlay {
  697. FlowPanel content = new FlowPanel();
  698. DebugButton[] sizes = { new DebugButton(null, "Small", "A"),
  699. new DebugButton(null, "Medium", "A"),
  700. new DebugButton(null, "Large", "A") };
  701. DebugButton[] modes = {
  702. new DebugButton(Icon.DEVMODE_OFF,
  703. "Debug only (causes page reload)"),
  704. new DebugButton(Icon.DEVMODE_ON,
  705. "DevMode (causes page reload)"),
  706. new DebugButton(Icon.DEVMODE_SUPER,
  707. "SuperDevMode (causes page reload)") };
  708. Menu() {
  709. super(true, true);
  710. setWidget(content);
  711. setStylePrimaryName(STYLENAME + "-menu");
  712. content.setStylePrimaryName(STYLENAME + "-menu-content");
  713. FlowPanel size = new FlowPanel();
  714. content.add(size);
  715. final ClickHandler sizeHandler = new ClickHandler() {
  716. @Override
  717. public void onClick(ClickEvent event) {
  718. for (int i = 0; i < sizes.length; i++) {
  719. Button b = sizes[i];
  720. if (b == event.getSource()) {
  721. setSize(i);
  722. }
  723. }
  724. hide();
  725. }
  726. };
  727. for (int i = 0; i < sizes.length; i++) {
  728. Button b = sizes[i];
  729. b.setStyleDependentName("size" + i, true);
  730. b.addClickHandler(sizeHandler);
  731. size.add(b);
  732. }
  733. FlowPanel mode = new FlowPanel();
  734. content.add(mode);
  735. final ClickHandler modeHandler = new ClickHandler() {
  736. @Override
  737. public void onClick(ClickEvent event) {
  738. for (int i = 0; i < modes.length; i++) {
  739. Button b = modes[i];
  740. if (b == event.getSource()) {
  741. setDevMode(i);
  742. }
  743. }
  744. hide();
  745. }
  746. };
  747. modes[getDevMode()].setActive(true);
  748. for (Button b : modes) {
  749. b.addClickHandler(modeHandler);
  750. mode.add(b);
  751. }
  752. Button reset = new DebugButton(Icon.RESET, "Restore defaults.",
  753. " Reset");
  754. reset.addClickHandler(new ClickHandler() {
  755. @Override
  756. public void onClick(ClickEvent event) {
  757. resetStoredState();
  758. readStoredState();
  759. hide();
  760. }
  761. });
  762. content.add(reset);
  763. }
  764. private void setSize(int size) {
  765. for (int i = 0; i < sizes.length; i++) {
  766. Button b = sizes[i];
  767. b.setStyleDependentName(STYLENAME_ACTIVE, i == size);
  768. }
  769. setFontSize(size);
  770. writeStoredState();
  771. }
  772. @Override
  773. public void show() {
  774. super.show();
  775. setSize(getFontSize());
  776. }
  777. private int getDevMode() {
  778. if (Location.getParameter("superdevmode") != null) {
  779. return 2;
  780. } else if (Location.getParameter("gwt.codesvr") != null) {
  781. return 1;
  782. } else {
  783. return 0;
  784. }
  785. }
  786. private void setDevMode(int mode) {
  787. UrlBuilder u = Location.createUrlBuilder();
  788. switch (mode) {
  789. case 2:
  790. u.setParameter("superdevmode", "");
  791. u.removeParameter("gwt.codesvr");
  792. break;
  793. case 1:
  794. u.setParameter("gwt.codesvr", "localhost:9997");
  795. u.removeParameter("superdevmode");
  796. break;
  797. default:
  798. u.removeParameter("gwt.codesvr");
  799. u.removeParameter("superdevmode");
  800. }
  801. Location.assign(u.buildString());
  802. }
  803. }
  804. /**
  805. * Handler for resizing and moving window, also updates cursor on mousemove.
  806. *
  807. * @since 7.1
  808. * @author Vaadin Ltd
  809. */
  810. protected class MouseHandler implements MouseMoveHandler, MouseDownHandler,
  811. NativePreviewHandler {
  812. boolean resizeLeft;
  813. boolean resizeRight;
  814. boolean resizeUp;
  815. boolean resizeDown;
  816. boolean move;
  817. boolean sizing;
  818. // dragging stopped, remove handler on next event
  819. boolean stop;
  820. HandlerRegistration dragHandler;
  821. int startX;
  822. int startY;
  823. int startW;
  824. int startH;
  825. int startTop;
  826. int startLeft;
  827. @Override
  828. public void onMouseMove(MouseMoveEvent event) {
  829. if (null == dragHandler) {
  830. updateResizeFlags(event);
  831. updateCursor();
  832. }
  833. }
  834. @Override
  835. public void onMouseDown(MouseDownEvent event) {
  836. if (event.getNativeButton() != NativeEvent.BUTTON_LEFT
  837. || dragHandler != null) {
  838. return;
  839. }
  840. updateResizeFlags(event);
  841. if (sizing || move) {
  842. // some os/browsers don't pass events trough scrollbars; hide
  843. // while dragging (esp. important for resize from right/bottom)
  844. content.getElement().getStyle().setOverflow(Overflow.HIDDEN);
  845. startX = event.getClientX();
  846. startY = event.getClientY();
  847. startW = content.getOffsetWidth();
  848. startH = content.getOffsetHeight();
  849. startTop = getPopupTop();
  850. startLeft = getPopupLeft();
  851. dragHandler = Event.addNativePreviewHandler(this);
  852. event.preventDefault();
  853. stop = false;
  854. }
  855. }
  856. @Override
  857. public void onPreviewNativeEvent(NativePreviewEvent event) {
  858. if (event.getTypeInt() == Event.ONMOUSEMOVE && !stop
  859. && hasMoved(event.getNativeEvent())) {
  860. int dx = event.getNativeEvent().getClientX() - startX;
  861. int dy = event.getNativeEvent().getClientY() - startY;
  862. if (sizing) {
  863. int minWidth = tabs.getOffsetWidth()
  864. + controls.getOffsetWidth();
  865. if (resizeLeft) {
  866. int w = startW - dx;
  867. if (w < minWidth) {
  868. w = minWidth;
  869. dx = startW - minWidth;
  870. }
  871. content.setWidth(w + "px");
  872. setPopupPosition(startLeft + dx, getPopupTop());
  873. } else if (resizeRight) {
  874. int w = startW + dx;
  875. if (w < minWidth) {
  876. w = minWidth;
  877. }
  878. content.setWidth(w + "px");
  879. }
  880. if (resizeUp) {
  881. int h = startH - dy;
  882. if (h < MIN_HEIGHT) {
  883. h = MIN_HEIGHT;
  884. dy = startH - MIN_HEIGHT;
  885. }
  886. content.setHeight(h + "px");
  887. setPopupPosition(getPopupLeft(), startTop + dy);
  888. } else if (resizeDown) {
  889. int h = startH + dy;
  890. if (h < MIN_HEIGHT) {
  891. h = MIN_HEIGHT;
  892. }
  893. content.setHeight(h + "px");
  894. }
  895. } else if (move) {
  896. setPopupPosition(startLeft + dx, startTop + dy);
  897. }
  898. event.cancel();
  899. } else if (event.getTypeInt() == Event.ONMOUSEUP) {
  900. stop = true;
  901. if (hasMoved(event.getNativeEvent())) {
  902. event.cancel();
  903. }
  904. } else if (event.getTypeInt() == Event.ONCLICK) {
  905. stop = true;
  906. if (hasMoved(event.getNativeEvent())) {
  907. event.cancel();
  908. }
  909. } else if (stop) {
  910. stop = false;
  911. dragHandler.removeHandler();
  912. dragHandler = null;
  913. sizing = false;
  914. move = false;
  915. // restore scrollbars
  916. content.getElement().getStyle().setOverflow(Overflow.AUTO);
  917. updateCursor();
  918. applyBounds();
  919. readPositionAndSize();
  920. writeStoredState();
  921. event.cancel();
  922. }
  923. }
  924. private boolean hasMoved(NativeEvent event) {
  925. return Math.abs(startX - event.getClientX()) > MOVE_TRESHOLD
  926. || Math.abs(startY - event.getClientY()) > MOVE_TRESHOLD;
  927. }
  928. private void updateCursor() {
  929. Element c = getElement();
  930. if (resizeLeft) {
  931. if (resizeUp) {
  932. c.getStyle().setCursor(Cursor.NW_RESIZE);
  933. } else if (resizeDown) {
  934. c.getStyle().setCursor(Cursor.SW_RESIZE);
  935. } else {
  936. c.getStyle().setCursor(Cursor.W_RESIZE);
  937. }
  938. } else if (resizeRight) {
  939. if (resizeUp) {
  940. c.getStyle().setCursor(Cursor.NE_RESIZE);
  941. } else if (resizeDown) {
  942. c.getStyle().setCursor(Cursor.SE_RESIZE);
  943. } else {
  944. c.getStyle().setCursor(Cursor.E_RESIZE);
  945. }
  946. } else if (resizeUp) {
  947. c.getStyle().setCursor(Cursor.N_RESIZE);
  948. } else if (resizeDown) {
  949. c.getStyle().setCursor(Cursor.S_RESIZE);
  950. } else if (move) {
  951. c.getStyle().setCursor(Cursor.MOVE);
  952. } else {
  953. c.getStyle().setCursor(Cursor.AUTO);
  954. }
  955. }
  956. protected void updateResizeFlags(MouseEvent event) {
  957. if (event.isShiftKeyDown()) {
  958. // resize from lower right
  959. resizeUp = false;
  960. resizeLeft = false;
  961. resizeRight = true;
  962. resizeDown = true;
  963. move = false;
  964. sizing = true;
  965. return;
  966. } else if (event.isAltKeyDown()) {
  967. // move it
  968. move = true;
  969. resizeUp = false;
  970. resizeLeft = false;
  971. resizeRight = false;
  972. resizeDown = false;
  973. sizing = false;
  974. return;
  975. }
  976. Element c = getElement();
  977. int w = c.getOffsetWidth();
  978. int h = c.getOffsetHeight();
  979. int x = event.getRelativeX(c);
  980. int y = event.getRelativeY(c);
  981. resizeLeft = x < HANDLE_SIZE && y > tabs.getOffsetHeight();
  982. resizeRight = (x > (w - HANDLE_SIZE) && y > tabs.getOffsetHeight())
  983. || (x > (w - 2 * HANDLE_SIZE) && y > (h - 2 * HANDLE_SIZE));
  984. resizeUp = y > tabs.getOffsetHeight()
  985. && y < tabs.getOffsetHeight() + HANDLE_SIZE;
  986. resizeDown = y > (h - HANDLE_SIZE)
  987. || (x > (w - 2 * HANDLE_SIZE) && y > (h - 2 * HANDLE_SIZE));
  988. move = !resizeDown && !resizeLeft && !resizeRight && !resizeUp
  989. && y < head.getOffsetHeight();
  990. sizing = resizeLeft || resizeRight || resizeUp || resizeDown;
  991. }
  992. }
  993. }