001package gwt.material.design.addins.client.window;
002
003/*
004 * #%L
005 * GwtMaterial
006 * %%
007 * Copyright (C) 2015 - 2016 GwtMaterialDesign
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023
024import com.google.gwt.dom.client.Document;
025import com.google.gwt.dom.client.Style;
026import com.google.gwt.event.dom.client.ClickEvent;
027import com.google.gwt.event.dom.client.ClickHandler;
028import com.google.gwt.event.dom.client.DoubleClickEvent;
029import com.google.gwt.event.dom.client.DoubleClickHandler;
030import com.google.gwt.event.logical.shared.*;
031import com.google.gwt.event.shared.HandlerRegistration;
032import com.google.gwt.user.client.ui.RootPanel;
033import com.google.gwt.user.client.ui.Widget;
034import gwt.material.design.addins.client.MaterialAddins;
035import gwt.material.design.addins.client.dnd.MaterialDnd;
036import gwt.material.design.addins.client.dnd.constants.Restriction;
037import gwt.material.design.client.MaterialDesignBase;
038import gwt.material.design.client.base.MaterialWidget;
039import gwt.material.design.client.base.mixin.ColorsMixin;
040import gwt.material.design.client.base.mixin.ToggleStyleMixin;
041import gwt.material.design.client.constants.IconType;
042import gwt.material.design.client.constants.WavesType;
043import gwt.material.design.client.ui.MaterialIcon;
044import gwt.material.design.client.ui.MaterialLink;
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 close the window. Also you can attached a tab component on its content.
051 *
052 * <h3>XML Namespace Declaration</h3>
053 * <pre>
054 * {@code
055 * xmlns:ma='urn:import:gwt.material.design.addins.client'
056 * }
057 * </pre>
058 *
059 * <h3>UiBinder Usage:</h3>
060 * <pre>
061 * {@code
062 *  <ma:window.MaterialWindow ui:field="window" />
063 * }
064 * </pre>
065 *
066 * <h3>UiBinder Usage:</h3>
067 * <pre>
068 * {@code
069 *  // Opening a window
070 *  window.openWindow();
071 *
072 *  // Closing a window
073 *  window.closeWindow();
074 * }
075 * </pre>
076 *
077 * @author kevzlou7979
078 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#window">Material Window</a>
079 */
080//@formatter:on
081public class MaterialWindow extends MaterialWidget implements HasCloseHandlers<Boolean>, HasOpenHandlers<Boolean>{
082
083    static {
084        if(MaterialAddins.isDebug()) {
085            MaterialDesignBase.injectCss(MaterialWindowDebugClientBundle.INSTANCE.windowCssDebug());
086        } else {
087            MaterialDesignBase.injectCss(MaterialWindowClientBundle.INSTANCE.windowCss());
088        }
089    }
090
091    private MaterialWidget window = new MaterialWidget(Document.get().createDivElement());
092    private MaterialWidget content = new MaterialWidget(Document.get().createDivElement());
093
094    // Toolbar elements
095    private MaterialWidget toolbar = new MaterialWidget(Document.get().createDivElement());
096    private MaterialIcon iconMaximize = new MaterialIcon(IconType.CHECK_BOX_OUTLINE_BLANK);
097    private MaterialIcon iconClose = new MaterialIcon(IconType.CLOSE);
098    private MaterialLink link = new MaterialLink();
099    private String title = "";
100    private String toolbarColor;
101
102    private final ColorsMixin<MaterialWidget> toolbarColorMixin = new ColorsMixin<>(toolbar);
103    private final ToggleStyleMixin<MaterialWidget> maximizeMixin = new ToggleStyleMixin<>(window, "maximize");
104    private final ToggleStyleMixin<MaterialWindow> closeMixin = new ToggleStyleMixin<>(this, "open");
105    private boolean maximize = true;
106    private boolean open = false;
107
108    private MaterialAnimation openAnimation;
109    private MaterialAnimation closeAnimation;
110
111
112    public MaterialWindow() {
113        super(Document.get().createDivElement(), "window-overlay");
114        window.setStyleName("window");
115        content.setStyleName("content");
116        super.add(window);
117        initWindow();
118    }
119
120    /**
121     * Builds the toolbar
122     */
123    protected void initWindow() {
124        toolbar.setStyleName("window-toolbar");
125        link.setStyleName("window-title");
126        iconClose.addStyleName("window-action");
127        iconMaximize.addStyleName("window-action");
128        iconClose.setCircle(true);
129        iconClose.setWaves(WavesType.DEFAULT);
130        iconMaximize.setCircle(true);
131        iconMaximize.setWaves(WavesType.DEFAULT);
132        toolbar.add(link);
133        toolbar.add(iconClose);
134        toolbar.add(iconMaximize);
135        toolbar.addDomHandler(new DoubleClickHandler() {
136            @Override
137            public void onDoubleClick(DoubleClickEvent event) {
138                toggleMaximize();
139                Document.get().getDocumentElement().getStyle().setCursor(Style.Cursor.DEFAULT);
140            }
141        }, DoubleClickEvent.getType());
142        window.add(toolbar);
143        window.add(content);
144
145        // Add handlers to action buttons
146        iconMaximize.addClickHandler(new ClickHandler() {
147            @Override
148            public void onClick(ClickEvent event) {
149                toggleMaximize();
150            }
151        });
152        iconClose.addClickHandler(new ClickHandler() {
153            @Override
154            public void onClick(ClickEvent event) {
155                if(open){
156                    openWindow();
157                    open = false;
158                }else{
159                    closeWindow();
160                    open = true;
161                }
162            }
163        });
164
165        // Add a draggable header
166        MaterialDnd dnd = new MaterialDnd();
167        dnd.setTarget(window);
168        dnd.setIgnoreFrom(".content, .window-action");
169        dnd.setRestriction(new Restriction(Restriction.Restrict.PARENT, true, -0.3, 0, 1.1, 1));
170    }
171
172    protected void toggleMaximize(){
173        if (maximize) {
174            setMaximize(true);
175            maximize = false;
176        } else {
177            setMaximize(false);
178            maximize = true;
179        }
180    }
181
182    @Override
183    public void add(Widget child) {
184        content.add(child);
185    }
186
187    @Override
188    public String getTitle() {
189        return title;
190    }
191
192    @Override
193    public void setTitle(String title) {
194        this.title = title;
195        link.setText(title);
196    }
197
198    public boolean isMaximize() {
199        return maximizeMixin.isOn();
200    }
201
202    public void setMaximize(boolean maximize) {
203        maximizeMixin.setOn(maximize);
204        if(maximizeMixin.isOn()){
205            iconMaximize.setIconType(IconType.FILTER_NONE);
206        }else{
207            iconMaximize.setIconType(IconType.CHECK_BOX_OUTLINE_BLANK);
208        }
209    }
210
211    /**
212     * Open the window
213     */
214    public void openWindow() {
215        if (!this.isAttached()) {
216            RootPanel.get().add(this);
217        }
218        this.open = false;
219        OpenEvent.fire(this, true);
220        if (openAnimation != null) {
221            openAnimation.animate(window);
222        }
223        closeMixin.setOn(true);
224    }
225
226    /**
227     * Close the window
228     */
229    public void closeWindow() {
230        this.open = true;
231        CloseEvent.fire(this, false);
232        if (closeAnimation == null) {
233            closeMixin.setOn(false);
234        } else {
235            closeAnimation.animate(window, new Runnable() {
236                @Override
237                public void run() {
238                    closeMixin.setOn(false);
239                }
240            });
241        }
242    }
243
244    public String getToolbarColor() {
245        return toolbarColor;
246    }
247
248    public void setToolbarColor(String toolbarColor) {
249        this.toolbarColor = toolbarColor;
250        toolbarColorMixin.setBackgroundColor(toolbarColor);
251    }
252
253    public void setOpenAnimation(final MaterialAnimation openAnimation) {
254        this.openAnimation = openAnimation;
255    }
256
257    public void setCloseAnimation(final MaterialAnimation closeAnimation) {
258        this.closeAnimation = closeAnimation;
259    }
260
261    @Override
262    public HandlerRegistration addCloseHandler(final CloseHandler<Boolean> handler) {
263        return addHandler(new CloseHandler<Boolean>() {
264            @Override
265            public void onClose(CloseEvent<Boolean> event) {
266                if(isEnabled()){
267                    handler.onClose(event);
268                }
269            }
270        }, CloseEvent.getType());
271    }
272
273    @Override
274    public HandlerRegistration addOpenHandler(final OpenHandler<Boolean> handler) {
275        return addHandler(new OpenHandler<Boolean>() {
276            @Override
277            public void onOpen(OpenEvent<Boolean> event) {
278                if(isEnabled()){
279                    handler.onOpen(event);
280                }
281            }
282        }, OpenEvent.getType());
283    }
284
285    @Override
286    public void setWidth(String width) {
287        window.setWidth(width);
288    }
289
290    @Override
291    public void setHeight(String height) {
292        window.setHeight(height);
293    }
294
295    @Override
296    public void setBackgroundColor(String bgColor) {
297        window.setBackgroundColor(bgColor);
298    }
299
300    public boolean isOpen() {
301        return open;
302    }
303}