1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.vaadin.gwt.client.tabsheet.widget;
35
36 import info.magnolia.ui.vaadin.gwt.client.tabsheet.event.ActiveTabChangedEvent;
37 import info.magnolia.ui.vaadin.gwt.client.tabsheet.event.ShowAllTabsEvent;
38 import info.magnolia.ui.vaadin.gwt.client.tabsheet.event.ShowAllTabsHandler;
39 import info.magnolia.ui.vaadin.gwt.client.tabsheet.event.TabCloseEvent;
40 import info.magnolia.ui.vaadin.gwt.client.tabsheet.event.TabCloseEventHandler;
41 import info.magnolia.ui.vaadin.gwt.client.tabsheet.tab.widget.MagnoliaTabLabel;
42 import info.magnolia.ui.vaadin.gwt.client.tabsheet.tab.widget.MagnoliaTabWidget;
43 import info.magnolia.ui.vaadin.gwt.client.tabsheet.util.CollectionUtil;
44
45 import java.util.Collections;
46 import java.util.HashMap;
47 import java.util.Iterator;
48 import java.util.LinkedList;
49 import java.util.List;
50 import java.util.Map;
51
52 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
53 import com.google.gwt.dom.client.NativeEvent;
54 import com.google.gwt.event.dom.client.ClickEvent;
55 import com.google.gwt.event.dom.client.ClickHandler;
56 import com.google.gwt.user.client.DOM;
57 import com.google.gwt.user.client.Element;
58 import com.google.gwt.user.client.ui.ComplexPanel;
59 import com.google.gwt.user.client.ui.DialogBox;
60 import com.google.gwt.user.client.ui.MenuBar;
61 import com.google.gwt.user.client.ui.MenuItem;
62 import com.google.gwt.user.client.ui.SimplePanel;
63 import com.google.gwt.user.client.ui.Widget;
64 import com.google.web.bindery.event.shared.EventBus;
65 import com.vaadin.client.ui.VButton;
66
67
68
69
70 public class TabBarWidget extends ComplexPanel {
71
72 private static final String SINGLE_TAB_CLASS_NAME = "single-tab";
73
74 private final List<MagnoliaTabLabel> tabLabels = new LinkedList<>();
75
76 private final Element tabContainer = DOM.createElement("ul");
77
78 private final EventBus eventBus;
79
80 private VShellShowAllTabLabel showAllTab;
81
82 private final HiddenTabsPopup hiddenTabsPopup;
83
84 private MagnoliaTabLabel activeTab;
85
86 public TabBarWidget(EventBus eventBus) {
87 this.eventBus = eventBus;
88 setElement(tabContainer);
89 setStyleName("nav");
90 addStyleDependentName("tabs");
91
92 hiddenTabsPopup = new HiddenTabsPopup(eventBus);
93 add(hiddenTabsPopup, tabContainer);
94 bindHandlers();
95 }
96
97 public void reArrangeTabVisibility() {
98 if (tabLabels.isEmpty()) {
99 return;
100 }
101
102 if (activeTab == null) {
103 activeTab = tabLabels.get(0);
104 }
105
106
107 hiddenTabsPopup.setVisible(true);
108 int toggleWidth = hiddenTabsPopup.getOffsetWidth();
109 int availableWidth = tabContainer.getOffsetWidth();
110
111
112 activeTab.setVisible(true);
113 availableWidth -= activeTab.getOffsetWidth();
114
115
116 if (showAllTab != null) {
117 availableWidth -= showAllTab.getOffsetWidth();
118 }
119
120
121 Map<MagnoliaTabLabel, Integer> tabWidths = new HashMap<>();
122 for (MagnoliaTabLabel tab : tabLabels) {
123 if (tab == activeTab) {
124 continue;
125 }
126 tab.setVisible(true);
127 tabWidths.put(tab, tab.getOffsetWidth());
128 }
129
130
131 Iterator<MagnoliaTabLabel> it = tabLabels.iterator();
132 boolean outOfSpace = false;
133 while (it.hasNext()) {
134 MagnoliaTabLabel tab = it.next();
135
136 if (tab == activeTab) {
137 continue;
138 }
139 if (!outOfSpace) {
140 int width = tabWidths.get(tab);
141 int maxWidth = Collections.max(tabWidths.values());
142
143 if ((!it.hasNext() && availableWidth >= width)
144
145
146 || (maxWidth + toggleWidth <= availableWidth)) {
147 tabWidths.remove(tab);
148 availableWidth -= width;
149 continue;
150 } else {
151 outOfSpace = true;
152 }
153 }
154
155 tab.setVisible(false);
156 }
157
158
159 hiddenTabsPopup.hide();
160 hiddenTabsPopup.menubar.clearItems();
161 it = tabLabels.iterator();
162 while (it.hasNext()) {
163 MagnoliaTabLabel tab = it.next();
164 if (!tab.isVisible()) {
165 hiddenTabsPopup.addTabLabel(tab);
166 }
167 }
168 hiddenTabsPopup.showControlIfNeeded();
169 }
170
171 private void bindHandlers() {
172 eventBus.addHandler(ActiveTabChangedEvent.TYPE, new ActiveTabChangedEvent.Handler() {
173 @Override
174 public void onActiveTabChanged(final ActiveTabChangedEvent event) {
175 final MagnoliaTabWidget tab = event.getTab();
176 final MagnoliaTabLabel label = tab.getLabel();
177 if (label != null) {
178 for (final MagnoliaTabLabel tabLabel : tabLabels) {
179 tabLabel.removeStyleName("active");
180 }
181 label.addStyleName("active");
182 showAll(false);
183 activeTab = label;
184 hiddenTabsPopup.menuWrapper.hide();
185 if (!label.isVisible()) {
186 reArrangeTabVisibility();
187 }
188 }
189 }
190 });
191
192 eventBus.addHandler(TabCloseEvent.TYPE, new TabCloseEventHandler() {
193 @Override
194 public void onTabClosed(TabCloseEvent event) {
195 final MagnoliaTabLabel tabLabel = event.getTab().getLabel();
196 boolean wasActive = tabLabel.getStyleName().contains("active");
197 if (wasActive) {
198 final MagnoliaTabLabel nextLabel = getNextLabel(tabLabel);
199 if (nextLabel != null) {
200 nextLabel.addStyleName("active");
201 activeTab = nextLabel;
202 }
203 }
204 tabLabels.remove(tabLabel);
205 remove(tabLabel);
206 updateSingleTabStyle();
207 reArrangeTabVisibility();
208 }
209 });
210
211 eventBus.addHandler(ShowAllTabsEvent.TYPE, new ShowAllTabsHandler() {
212
213 @Override
214 public void onShowAllTabs(ShowAllTabsEvent event) {
215 for (final MagnoliaTabLabel tabLabel : tabLabels) {
216 tabLabel.removeStyleName("active");
217 }
218 showAll(true);
219 }
220 });
221 }
222
223 protected MagnoliaTabLabel getNextLabel(final MagnoliaTabLabel label) {
224 return CollectionUtil.getNext(tabLabels, label);
225 }
226
227 public void addTabLabel(MagnoliaTabLabel label) {
228 label.setEventBus(eventBus);
229 if (!tabLabels.contains(label)) {
230 tabLabels.add(label);
231
232 insert(label, tabContainer, tabLabels.size() - 1, true);
233 updateSingleTabStyle();
234 }
235 }
236
237 public void updateSingleTabStyle() {
238 if (tabLabels.size() <= 1) {
239 tabContainer.addClassName(SINGLE_TAB_CLASS_NAME);
240 } else {
241 tabContainer.removeClassName(SINGLE_TAB_CLASS_NAME);
242 }
243 }
244
245 public void addShowAllTab(boolean showAll, String label) {
246 if (showAll && showAllTab == null) {
247 showAllTab = new VShellShowAllTabLabel(label);
248 add(showAllTab, getElement());
249 } else if (!showAll && showAllTab != null) {
250 remove(showAllTab);
251 showAllTab = null;
252 }
253 reArrangeTabVisibility();
254 }
255
256 private class VShellShowAllTabLabel extends SimplePanel {
257
258 private final VButton textWrapper = new VButton();
259
260 public VShellShowAllTabLabel(String label) {
261 super(DOM.createElement("li"));
262 addStyleName("show-all");
263 textWrapper.getElement().setInnerHTML(label);
264 textWrapper.getElement().setClassName("tab-title");
265 this.add(textWrapper);
266 }
267
268 @Override
269 protected void onLoad() {
270 super.onLoad();
271 bindHandlers();
272 }
273
274 private void bindHandlers() {
275 addDomHandler(new ClickHandler() {
276 @Override
277 public void onClick(ClickEvent event) {
278 onClickGeneric(event.getNativeEvent());
279 }
280 }, ClickEvent.getType());
281
282 textWrapper.addClickHandler(new ClickHandler() {
283 @Override
284 public void onClick(ClickEvent event) {
285 textWrapper.setFocus(false);
286 onClickGeneric(event.getNativeEvent());
287 }
288 });
289 }
290
291 private void onClickGeneric(NativeEvent nativeEvent) {
292 eventBus.fireEvent(new ShowAllTabsEvent());
293 nativeEvent.stopPropagation();
294 }
295
296 }
297
298
299
300
301 static class HiddenTabsPopup extends Widget {
302
303 private final DialogBox menuWrapper = new DialogBox(true);
304 private final HiddenTabsMenuBar menubar = new HiddenTabsMenuBar();
305 private final EventBus eventBus;
306
307 public HiddenTabsPopup(EventBus eventBus) {
308 this.eventBus = eventBus;
309
310 setElement(DOM.createElement("li"));
311 addStyleName("icon-arrow2_e");
312 addStyleName("hidden-tabs-popup-button");
313 menuWrapper.add(menubar);
314 menuWrapper.setStyleName("context-menu-wrapper");
315
316
317 setVisible(false);
318 }
319
320 @Override
321 protected void onLoad() {
322 super.onLoad();
323 bindHandlers();
324 }
325
326 public void addTabLabel(final MagnoliaTabLabel label) {
327 final MenuItem item = menubar.addItem(label.getCaption(), new ScheduledCommand() {
328 @Override
329 public void execute() {
330 menuWrapper.hide();
331 eventBus.fireEvent(new ActiveTabChangedEvent(label.getTab()));
332 }
333 });
334 item.addStyleName("menu-item");
335 }
336
337 private void bindHandlers() {
338 addDomHandler(new ClickHandler() {
339 @Override
340 public void onClick(ClickEvent event) {
341 event.preventDefault();
342 event.stopPropagation();
343 menuWrapper.setPopupPosition(getAbsoluteLeft() + getOffsetWidth(), getAbsoluteTop());
344 menuWrapper.show();
345 }
346
347 }, ClickEvent.getType());
348 }
349
350 public void hide() {
351 menuWrapper.hide();
352 setVisible(false);
353 }
354
355 public void showControlIfNeeded() {
356 setVisible(!menubar.isEmpty());
357 }
358
359 private class HiddenTabsMenuBar extends MenuBar {
360
361 public HiddenTabsMenuBar() {
362 super(true);
363 setStyleName("context-menu");
364 addStyleName("hidden-tabs-menu");
365 }
366
367 public boolean isEmpty() {
368 return super.getItems().isEmpty();
369 }
370 }
371 }
372
373 public void showAll(boolean showAll) {
374 if (showAllTab != null) {
375 if (showAll) {
376 showAllTab.addStyleName("active");
377 } else {
378 if (showAllTab.getStyleName().contains("active")) {
379 showAllTab.removeStyleName("active");
380 }
381 }
382 }
383 }
384
385 }