/* * Copyright (c) 2011 PonySDK * Owners: * Luciano Broussal <luciano.broussal AT gmail.com> * Mathieu Barbier <mathieu.barbier AT gmail.com> * Nicolas Ciaravola <nicolas.ciaravola.pro AT gmail.com> * * WebSite: * http://code.google.com/p/pony-sdk/ * * 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.ponysdk.core.ui.basic; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Objects; import javax.json.JsonObject; import com.ponysdk.core.model.ClientToServerModel; import com.ponysdk.core.model.HandlerModel; import com.ponysdk.core.model.ServerToClientModel; import com.ponysdk.core.model.WidgetType; import com.ponysdk.core.ui.basic.event.HasPBeforeSelectionHandlers; import com.ponysdk.core.ui.basic.event.HasPSelectionHandlers; import com.ponysdk.core.ui.basic.event.PBeforeSelectionEvent; import com.ponysdk.core.ui.basic.event.PBeforeSelectionHandler; import com.ponysdk.core.ui.basic.event.PSelectionEvent; import com.ponysdk.core.ui.basic.event.PSelectionHandler; import com.ponysdk.core.ui.model.ServerBinaryModel; /** * A panel that represents a tabbed set of pages, each of which contains another * widget. Its child widgets are shown as the user selects the various tabs * associated with them. The tabs can contain arbitrary text, HTML, or widgets. * <p> * This widget will <em>only</em> work in standards mode, which requires that * the HTML page in which it is run have an explicit <!DOCTYPE> * declaration. * </p> * <h3>CSS Style Rules</h3> * <dl> * <dt>.gwt-TabLayoutPanel * <dd>the panel itself * <dt>.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabs * <dd>the tab bar element * <dt>.gwt-TabLayoutPanel .gwt-TabLayoutPanelTab * <dd>an individual tab * <dt>.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabInner * <dd>an element nested in each tab (useful for styling) * <dt>.gwt-TabLayoutPanel .gwt-TabLayoutPanelContent * <dd>applied to all child content widgets * </dl> * <p> * The children of a TabLayoutPanel element are laid out in <g:tab> elements. * Each tab can have one widget child and one of two types of header elements. A * <g:header> element can hold html, or a <g:customHeader> element can * hold a widget. */ public class PTabLayoutPanel extends PComplexPanel implements HasPBeforeSelectionHandlers<Integer>, HasPSelectionHandlers<Integer>, PSelectionHandler<Integer>, PAnimatedLayout { private final List<PBeforeSelectionHandler<Integer>> beforeSelectionHandlers = new ArrayList<>(); private final List<PSelectionHandler<Integer>> selectionHandlers = new ArrayList<>(); private Integer selectedItemIndex; private Duration animationDuration; protected PTabLayoutPanel() { } @Override protected void init0() { super.init0(); saveAddHandler(HandlerModel.HANDLER_SELECTION); } @Override protected WidgetType getWidgetType() { return WidgetType.TAB_LAYOUT_PANEL; } public void insert(final IsPWidget widget, final String tabText, final int beforeIndex) { insert(asWidgetOrNull(widget), tabText, beforeIndex); } public void insert(final IsPWidget widget, final IsPWidget tabWidget, final int beforeIndex) { insert(asWidgetOrNull(widget), asWidgetOrNull(tabWidget), beforeIndex); } public void insert(final PWidget child, final PWidget tabWidget, final int beforeIndex) { assertNotMe(child); if (child.getWindow() == null || child.getWindow() == window) { child.removeFromParent(); children.insert(child, beforeIndex); adopt(child); tabWidget.attach(window); child.attach(window); child.saveAdd(child.getID(), ID, new ServerBinaryModel(ServerToClientModel.TAB_WIDGET, tabWidget.getID()), new ServerBinaryModel(ServerToClientModel.BEFORE_INDEX, beforeIndex)); } else { throw new IllegalAccessError("Widget " + child + " already attached to an other window, current window : " + child.getWindow() + ", new window : " + window); } } public void insert(final PWidget child, final String tabText, final int beforeIndex) { if (child.getWindow() == null || child.getWindow() == window) { child.removeFromParent(); children.insert(child, beforeIndex); adopt(child); child.attach(window); child.saveAdd(child.getID(), ID, new ServerBinaryModel(ServerToClientModel.TAB_TEXT, tabText), new ServerBinaryModel(ServerToClientModel.BEFORE_INDEX, beforeIndex)); } else { throw new IllegalAccessError("Widget " + child + " already attached to an other window, current window : " + child.getWindow() + ", new window : " + window); } } public void add(final IsPWidget w, final IsPWidget tabWidget) { add(asWidgetOrNull(w), asWidgetOrNull(tabWidget)); } public void add(final IsPWidget w, final String tabText) { add(asWidgetOrNull(w), tabText); } public void add(final PWidget widget, final String tabText) { insert(widget, tabText, getWidgetCount()); } public void add(final PWidget widget, final PWidget tabWidget) { insert(widget, tabWidget, getWidgetCount()); } public void selectTab(final int index) { if (index >= getWidgetCount()) throw new IndexOutOfBoundsException(); this.selectedItemIndex = index; saveUpdate(writer -> writer.write(ServerToClientModel.SELECTED_INDEX, index)); } @Override public void add(final PWidget w) { throw new UnsupportedOperationException("A tabText parameter must be specified with add()."); } @Override public void addBeforeSelectionHandler(final PBeforeSelectionHandler<Integer> handler) { beforeSelectionHandlers.add(handler); saveAddHandler(HandlerModel.HANDLER_BEFORE_SELECTION); } @Override public void removeBeforeSelectionHandler(final PBeforeSelectionHandler<Integer> handler) { beforeSelectionHandlers.remove(handler); } @Override public void addSelectionHandler(final PSelectionHandler<Integer> handler) { selectionHandlers.add(handler); } @Override public void removeSelectionHandler(final PSelectionHandler<Integer> handler) { selectionHandlers.remove(handler); } @Override public void onClientData(final JsonObject instruction) { if (instruction.containsKey(ClientToServerModel.HANDLER_SELECTION.toStringValue())) { for (final PSelectionHandler<Integer> handler : selectionHandlers) { handler.onSelection( new PSelectionEvent<>(this, instruction.getInt(ClientToServerModel.HANDLER_SELECTION.toStringValue()))); } } else if (instruction.containsKey(ClientToServerModel.HANDLER_BEFORE_SELECTION.toStringValue())) { for (final PBeforeSelectionHandler<Integer> handler : beforeSelectionHandlers) { handler.onBeforeSelection(new PBeforeSelectionEvent<>(this, instruction.getInt(ClientToServerModel.HANDLER_BEFORE_SELECTION.toStringValue()))); } } else { super.onClientData(instruction); } } @Override public void onSelection(final PSelectionEvent<Integer> event) { selectedItemIndex = event.getSelectedItem(); } public Integer getSelectedItemIndex() { return selectedItemIndex; } /** * Set whether or not transitions slide in vertically or horizontally. * * @param isVertical * true for vertical transitions, false for horizontal */ public void setAnimationVertical(final boolean isVertical) { saveUpdate(ServerToClientModel.VERTICAL, isVertical); } @Override public void animate(final Duration duration) { saveUpdate(writer -> writer.write(ServerToClientModel.ANIMATE, (int) duration.toMillis())); } public Duration getAnimationDuration() { return animationDuration; } /** * Set the duration of the animated transition between tabs. */ public void setAnimationDuration(final Duration duration) { if (Objects.equals(this.animationDuration, duration)) return; this.animationDuration = duration; saveUpdate(ServerToClientModel.ANIMATION_DURATION, (int) duration.toMillis()); } }