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 if (event.getTab() == null) return;
176 final MagnoliaTabWidget tab = event.getTab();
177 final MagnoliaTabLabel label = tab.getLabel();
178 if (label != null) {
179 for (final MagnoliaTabLabel tabLabel : tabLabels) {
180 tabLabel.removeStyleName("active");
181 }
182 label.addStyleName("active");
183 showAll(false);
184 activeTab = label;
185 hiddenTabsPopup.menuWrapper.hide();
186 if (!label.isVisible()) {
187 reArrangeTabVisibility();
188 }
189 }
190 }
191 });
192
193 eventBus.addHandler(TabCloseEvent.TYPE, new TabCloseEventHandler() {
194 @Override
195 public void onTabClosed(TabCloseEvent event) {
196 final MagnoliaTabLabel tabLabel = event.getTab().getLabel();
197 boolean wasActive = tabLabel.getStyleName().contains("active");
198 if (wasActive) {
199 final MagnoliaTabLabel nextLabel = getNextLabel(tabLabel);
200 if (nextLabel != null) {
201 nextLabel.addStyleName("active");
202 activeTab = nextLabel;
203 }
204 }
205 tabLabels.remove(tabLabel);
206 remove(tabLabel);
207 updateSingleTabStyle();
208 reArrangeTabVisibility();
209 }
210 });
211
212 eventBus.addHandler(ShowAllTabsEvent.TYPE, new ShowAllTabsHandler() {
213
214 @Override
215 public void onShowAllTabs(ShowAllTabsEvent event) {
216 for (final MagnoliaTabLabel tabLabel : tabLabels) {
217 tabLabel.removeStyleName("active");
218 }
219 showAll(true);
220 }
221 });
222 }
223
224 protected MagnoliaTabLabel getNextLabel(final MagnoliaTabLabel label) {
225 return CollectionUtil.getNext(tabLabels, label);
226 }
227
228 public void addTabLabel(MagnoliaTabLabel label) {
229 label.setEventBus(eventBus);
230 if (!tabLabels.contains(label)) {
231 tabLabels.add(label);
232
233 insert(label, tabContainer, tabLabels.size() - 1, true);
234 updateSingleTabStyle();
235 }
236 }
237
238 public void updateSingleTabStyle() {
239 if (tabLabels.size() <= 1) {
240 tabContainer.addClassName(SINGLE_TAB_CLASS_NAME);
241 } else {
242 tabContainer.removeClassName(SINGLE_TAB_CLASS_NAME);
243 }
244 }
245
246 public void addShowAllTab(boolean showAll, String label) {
247 if (showAll && showAllTab == null) {
248 showAllTab = new VShellShowAllTabLabel(label);
249 add(showAllTab, getElement());
250 } else if (!showAll && showAllTab != null) {
251 remove(showAllTab);
252 showAllTab = null;
253 }
254 reArrangeTabVisibility();
255 }
256
257 private class VShellShowAllTabLabel extends SimplePanel {
258
259 private final VButton textWrapper = new VButton();
260
261 public VShellShowAllTabLabel(String label) {
262 super(DOM.createElement("li"));
263 addStyleName("show-all");
264 textWrapper.getElement().setInnerHTML(label);
265 textWrapper.getElement().setClassName("tab-title");
266 this.add(textWrapper);
267 }
268
269 @Override
270 protected void onLoad() {
271 super.onLoad();
272 bindHandlers();
273 }
274
275 private void bindHandlers() {
276 addDomHandler(new ClickHandler() {
277 @Override
278 public void onClick(ClickEvent event) {
279 onClickGeneric(event.getNativeEvent());
280 }
281 }, ClickEvent.getType());
282
283 textWrapper.addClickHandler(new ClickHandler() {
284 @Override
285 public void onClick(ClickEvent event) {
286 textWrapper.setFocus(false);
287 onClickGeneric(event.getNativeEvent());
288 }
289 });
290 }
291
292 private void onClickGeneric(NativeEvent nativeEvent) {
293 eventBus.fireEvent(new ShowAllTabsEvent());
294 nativeEvent.stopPropagation();
295 }
296
297 }
298
299
300
301
302 static class HiddenTabsPopup extends Widget {
303
304 private final DialogBox menuWrapper = new DialogBox(true);
305 private final HiddenTabsMenuBar menubar = new HiddenTabsMenuBar();
306 private final EventBus eventBus;
307
308 public HiddenTabsPopup(EventBus eventBus) {
309 this.eventBus = eventBus;
310
311 setElement(DOM.createElement("li"));
312 addStyleName("icon-arrow2_e");
313 addStyleName("hidden-tabs-popup-button");
314 menuWrapper.add(menubar);
315 menuWrapper.setStyleName("context-menu-wrapper");
316
317
318 setVisible(false);
319 }
320
321 @Override
322 protected void onLoad() {
323 super.onLoad();
324 bindHandlers();
325 }
326
327 public void addTabLabel(final MagnoliaTabLabel label) {
328 final MenuItem item = menubar.addItem(label.getCaption(), new ScheduledCommand() {
329 @Override
330 public void execute() {
331 menuWrapper.hide();
332 eventBus.fireEvent(new ActiveTabChangedEvent(label.getTab()));
333 }
334 });
335 item.addStyleName("menu-item");
336 }
337
338 private void bindHandlers() {
339 addDomHandler(new ClickHandler() {
340 @Override
341 public void onClick(ClickEvent event) {
342 event.preventDefault();
343 event.stopPropagation();
344 menuWrapper.setPopupPosition(getAbsoluteLeft() + getOffsetWidth(), getAbsoluteTop());
345 menuWrapper.show();
346 }
347
348 }, ClickEvent.getType());
349 }
350
351 public void hide() {
352 menuWrapper.hide();
353 setVisible(false);
354 }
355
356 public void showControlIfNeeded() {
357 setVisible(!menubar.isEmpty());
358 }
359
360 private class HiddenTabsMenuBar extends MenuBar {
361
362 public HiddenTabsMenuBar() {
363 super(true);
364 setStyleName("context-menu");
365 addStyleName("hidden-tabs-menu");
366 }
367
368 public boolean isEmpty() {
369 return super.getItems().isEmpty();
370 }
371 }
372 }
373
374 public void showAll(boolean showAll) {
375 if (showAllTab != null) {
376 if (showAll) {
377 showAllTab.addStyleName("active");
378 } else {
379 if (showAllTab.getStyleName().contains("active")) {
380 showAllTab.removeStyleName("active");
381 }
382 }
383 }
384 }
385
386 }