001/* 002 * #%L 003 * GwtMaterial 004 * %% 005 * Copyright (C) 2015 - 2017 GwtMaterialDesign 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package gwt.material.design.addins.client.window; 021 022import com.google.gwt.core.client.Scheduler; 023import com.google.gwt.dom.client.Document; 024import com.google.gwt.dom.client.Element; 025import com.google.gwt.dom.client.Style; 026import com.google.gwt.event.logical.shared.*; 027import com.google.gwt.event.shared.HandlerRegistration; 028import com.google.gwt.user.client.ui.RootPanel; 029import com.google.gwt.user.client.ui.UIObject; 030import com.google.gwt.user.client.ui.Widget; 031import gwt.material.design.addins.client.MaterialAddins; 032import gwt.material.design.addins.client.base.constants.AddinsCssName; 033import gwt.material.design.addins.client.dnd.MaterialDnd; 034import gwt.material.design.addins.client.dnd.constants.Restriction; 035import gwt.material.design.addins.client.dnd.js.JsDragOptions; 036import gwt.material.design.client.MaterialDesignBase; 037import gwt.material.design.client.base.MaterialWidget; 038import gwt.material.design.client.base.mixin.ToggleStyleMixin; 039import gwt.material.design.client.constants.Color; 040import gwt.material.design.client.constants.IconType; 041import gwt.material.design.client.constants.WavesType; 042import gwt.material.design.client.ui.MaterialIcon; 043import gwt.material.design.client.ui.MaterialLink; 044import gwt.material.design.client.ui.MaterialPanel; 045import gwt.material.design.client.ui.animate.MaterialAnimation; 046 047//@formatter:off 048 049/** 050 * Window is another kind of Modal but it has a header toolbar for maximizing and 051 * close the window. Also you can attached a tab component on its content. 052 * <p> 053 * <h3>XML Namespace Declaration</h3> 054 * <pre> 055 * {@code 056 * xmlns:ma='urn:import:gwt.material.design.addins.client' 057 * } 058 * </pre> 059 * <p> 060 * <h3>UiBinder Usage:</h3> 061 * <pre> 062 * {@code 063 * <ma:window.MaterialWindow ui:field="windowContainer" /> 064 * } 065 * </pre> 066 * <p> 067 * <h3>UiBinder Usage:</h3> 068 * <pre> 069 * {@code 070 * // Opening a window 071 * windowContainer.open(); 072 * 073 * // Closing a window 074 * windowContainer.close(); 075 * } 076 * </pre> 077 * 078 * @author kevzlou7979 079 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#window">Material Window</a> 080 */ 081//@formatter:on 082public class MaterialWindow extends MaterialPanel implements HasCloseHandlers<Boolean>, HasOpenHandlers<Boolean> { 083 084 static { 085 if (MaterialAddins.isDebug()) { 086 MaterialDesignBase.injectCss(MaterialWindowDebugClientBundle.INSTANCE.windowCssDebug()); 087 } else { 088 MaterialDesignBase.injectCss(MaterialWindowClientBundle.INSTANCE.windowCss()); 089 } 090 } 091 092 private static MaterialPanel windowOverlay; 093 private static int windowCount = 0; 094 private boolean preventClose; 095 private MaterialAnimation openAnimation; 096 private MaterialAnimation closeAnimation; 097 private MaterialDnd dnd; 098 private MaterialPanel content = new MaterialPanel(); 099 private MaterialLink labelTitle = new MaterialLink(); 100 private MaterialPanel toolbar = new MaterialPanel(); 101 private MaterialIcon iconMaximize = new MaterialIcon(IconType.CHECK_BOX_OUTLINE_BLANK); 102 private MaterialIcon iconClose = new MaterialIcon(IconType.CLOSE); 103 private HandlerRegistration toolbarAttachHandler; 104 105 private ToggleStyleMixin<MaterialWidget> maximizeMixin; 106 private ToggleStyleMixin<MaterialWindow> openMixin; 107 108 public MaterialWindow() { 109 super(AddinsCssName.WINDOW); 110 111 content.setStyleName(AddinsCssName.CONTENT); 112 toolbar.setStyleName(AddinsCssName.WINDOW_TOOLBAR); 113 labelTitle.setStyleName(AddinsCssName.WINDOW_TITLE); 114 iconClose.addStyleName(AddinsCssName.WINDOW_ACTION); 115 iconMaximize.addStyleName(AddinsCssName.WINDOW_ACTION); 116 117 iconClose.setCircle(true); 118 iconClose.setWaves(WavesType.DEFAULT); 119 120 iconMaximize.setCircle(true); 121 iconMaximize.setWaves(WavesType.DEFAULT); 122 123 toolbar.add(labelTitle); 124 toolbar.add(iconClose); 125 toolbar.add(iconMaximize); 126 127 super.add(toolbar); 128 super.add(content); 129 130 setTop(100); 131 132 // Add a draggable header 133 dnd = buildDnd(); 134 } 135 136 public MaterialWindow(String title) { 137 this(); 138 setTitle(title); 139 } 140 141 public MaterialWindow(String title, Color backgroundColor, Color textColor) { 142 this(title); 143 setBackgroundColor(backgroundColor); 144 setTextColor(textColor); 145 } 146 147 public MaterialWindow(String title, Color backgroundColor, Color textColor, Color toolbarColor) { 148 this(title, backgroundColor, textColor); 149 setToolbarColor(toolbarColor); 150 } 151 152 @Override 153 protected void onLoad() { 154 super.onLoad(); 155 156 // Add handlers to action buttons 157 registerHandler(iconMaximize.addClickHandler(event -> toggleMaximize())); 158 159 registerHandler(toolbar.addDoubleClickHandler(event -> { 160 toggleMaximize(); 161 Document.get().getDocumentElement().getStyle().setCursor(Style.Cursor.DEFAULT); 162 })); 163 164 registerHandler(iconClose.addClickHandler(event -> { 165 if (!preventClose) { 166 if (!isOpen()) { 167 open(); 168 } else { 169 close(); 170 } 171 } 172 })); 173 } 174 175 /** 176 * Override to provide custom options for window drag'n'drop 177 */ 178 protected JsDragOptions buildDragOptions() { 179 return JsDragOptions.create(new Restriction("body", true, 0, 0, 1.2, 1)); 180 } 181 182 /** 183 * Override to provide custom {@link MaterialDnd} instance. Default implementation will construct {@link MaterialDnd} 184 * using options provided by {@link #buildDragOptions()} and will ignore drag events from content portion of 185 * the window ({@link AddinsCssName#CONTENT}) as well from action buttons (close, maximize and other {@link AddinsCssName#WINDOW_ACTION}. 186 */ 187 protected MaterialDnd buildDnd() { 188 MaterialDnd dnd = MaterialDnd.draggable(this, buildDragOptions()); 189 dnd.ignoreFrom(".content, .window-action"); 190 return dnd; 191 } 192 193 protected void onClose() { 194 195 } 196 197 protected void toggleMaximize() { 198 setMaximize(!isMaximized()); 199 } 200 201 @Override 202 public void add(Widget child) { 203 content.add(child); 204 } 205 206 @Override 207 public boolean remove(Widget w) { 208 return content.remove(w); 209 } 210 211 @Override 212 public void insert(Widget child, int beforeIndex) { 213 content.insert(child, beforeIndex); 214 } 215 216 @Override 217 public void clear() { 218 content.clear(); 219 } 220 221 @Override 222 public String getTitle() { 223 return labelTitle.getTitle(); 224 } 225 226 @Override 227 public void setTitle(String title) { 228 labelTitle.setText(title); 229 } 230 231 public boolean isMaximized() { 232 return getMaximizeMixin().isOn(); 233 } 234 235 public void setMaximize(boolean maximize) { 236 getMaximizeMixin().setOn(maximize); 237 if (getMaximizeMixin().isOn()) { 238 iconMaximize.setIconType(IconType.FILTER_NONE); 239 } else { 240 iconMaximize.setIconType(IconType.CHECK_BOX_OUTLINE_BLANK); 241 } 242 } 243 244 public static boolean isOverlay() { 245 return windowOverlay != null && windowOverlay.isAttached(); 246 } 247 248 public static void setOverlay(boolean overlay) { 249 if(overlay) { 250 if(windowOverlay == null) { 251 windowOverlay = new MaterialPanel(AddinsCssName.WINDOW_OVERLAY); 252 } 253 } else { 254 if(windowOverlay != null) { 255 windowOverlay.removeFromParent(); 256 windowOverlay = null; 257 } 258 } 259 } 260 261 /** 262 * Open the window. 263 */ 264 public void open() { 265 if (!isAttached()) { 266 RootPanel.get().add(this); 267 } 268 windowCount++; 269 if(windowOverlay != null && !windowOverlay.isAttached()) { 270 RootPanel.get().add(windowOverlay); 271 } 272 273 if (openAnimation == null) { 274 getOpenMixin().setOn(true); 275 OpenEvent.fire(this, true); 276 } else { 277 setOpacity(0); 278 Scheduler.get().scheduleDeferred(() -> { 279 getOpenMixin().setOn(true); 280 openAnimation.animate(this, () -> OpenEvent.fire(this, true)); 281 }); 282 } 283 } 284 285 /** 286 * Close the window. 287 */ 288 public void close() { 289 // Turn back the cursor to POINTER 290 RootPanel.get().getElement().getStyle().setCursor(Style.Cursor.DEFAULT); 291 292 windowCount--; 293 if (closeAnimation == null) { 294 getOpenMixin().setOn(false); 295 if(windowOverlay != null && windowOverlay.isAttached() && windowCount < 1) { 296 windowOverlay.removeFromParent(); 297 } 298 CloseEvent.fire(this, false); 299 } else { 300 closeAnimation.animate(this, () -> { 301 getOpenMixin().setOn(false); 302 if(windowOverlay != null && windowOverlay.isAttached() && windowCount < 1) { 303 windowOverlay.removeFromParent(); 304 } 305 CloseEvent.fire(this, false); 306 }); 307 } 308 } 309 310 public Color getToolbarColor() { 311 return toolbar.getBackgroundColor(); 312 } 313 314 public void setToolbarColor(Color toolbarColor) { 315 if (toolbarAttachHandler != null) { 316 toolbarAttachHandler.removeHandler(); 317 toolbarAttachHandler = null; 318 } 319 320 if (toolbarColor != null) { 321 if (toolbar.isAttached()) { 322 toolbar.setBackgroundColor(toolbarColor); 323 } else { 324 if (toolbarAttachHandler == null) { 325 toolbarAttachHandler = registerHandler(toolbar.addAttachHandler(attachEvent -> toolbar.setBackgroundColor(toolbarColor))); 326 } 327 } 328 } 329 } 330 331 @Override 332 public void setBackgroundColor(Color bgColor) { 333 content.setBackgroundColor(bgColor); 334 } 335 336 @Override 337 public Color getBackgroundColor() { 338 return content.getBackgroundColor(); 339 } 340 341 public void setOpenAnimation(final MaterialAnimation openAnimation) { 342 this.openAnimation = openAnimation; 343 } 344 345 public void setCloseAnimation(final MaterialAnimation closeAnimation) { 346 this.closeAnimation = closeAnimation; 347 } 348 349 @Override 350 public HandlerRegistration addCloseHandler(final CloseHandler<Boolean> handler) { 351 return addHandler(handler, CloseEvent.getType()); 352 } 353 354 @Override 355 public HandlerRegistration addOpenHandler(final OpenHandler<Boolean> handler) { 356 return addHandler(handler, OpenEvent.getType()); 357 } 358 359 public boolean isOpen() { 360 return getOpenMixin().isOn(); 361 } 362 363 /** 364 * @deprecated can now reference the {@link MaterialWindow} directly. 365 */ 366 @Deprecated 367 public MaterialWidget getContainer() { 368 return this; 369 } 370 371 public MaterialWidget getToolbar() { 372 return toolbar; 373 } 374 375 public MaterialWidget getContent() { 376 return content; 377 } 378 379 public MaterialIcon getIconMaximize() { 380 return iconMaximize; 381 } 382 383 public MaterialIcon getIconClose() { 384 return iconClose; 385 } 386 387 public MaterialLink getLabelTitle() { 388 return labelTitle; 389 } 390 391 public static MaterialPanel getWindowOverlay() { 392 return windowOverlay; 393 } 394 395 @Override 396 public void setPadding(double padding) { 397 content.setPadding(padding); 398 } 399 400 @Override 401 public void setPaddingTop(double padding) { 402 content.setPaddingTop(padding); 403 } 404 405 @Override 406 public void setPaddingLeft(double padding) { 407 content.setPaddingTop(padding); 408 } 409 410 @Override 411 public void setPaddingRight(double padding) { 412 content.setPaddingRight(padding); 413 } 414 415 @Override 416 public void setPaddingBottom(double padding) { 417 content.setPaddingBottom(padding); 418 } 419 420 public boolean isPreventClose() { 421 return preventClose; 422 } 423 424 public void setPreventClose(boolean preventClose) { 425 this.preventClose = preventClose; 426 } 427 428 /** 429 * Set the area for the drag and drop, can be an {@link Element} 430 * or a {@link String} selector. 431 */ 432 public void setDndArea(Object dndArea) { 433 if(dndArea instanceof UIObject) { 434 dndArea = ((UIObject) dndArea).getElement(); 435 } 436 if(dnd != null) { 437 dnd.draggable(JsDragOptions.create(new Restriction(dndArea, true, 0, 0, 1.2, 1))); 438 } 439 } 440 441 protected ToggleStyleMixin<MaterialWidget> getMaximizeMixin() { 442 if (maximizeMixin == null) { 443 maximizeMixin = new ToggleStyleMixin<>(this, AddinsCssName.MAXIMIZE); 444 } 445 return maximizeMixin; 446 } 447 448 protected ToggleStyleMixin<MaterialWindow> getOpenMixin() { 449 if (openMixin == null) { 450 openMixin = new ToggleStyleMixin<>(this, AddinsCssName.OPEN); 451 } 452 return openMixin; 453 } 454 455 public MaterialDnd getDnd() { 456 return dnd; 457 } 458}