/**
* Copyright 2010 Google Inc.
*
* 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 org.waveprotocol.wave.client.widget.overflowpanel;
import org.waveprotocol.wave.client.scheduler.Scheduler.Priority;
import org.waveprotocol.wave.client.scheduler.Scheduler.Task;
import org.waveprotocol.wave.client.scheduler.SchedulerInstance;
/**
* Decorator which can be attached to an OverflowPanel, and makes sure that overflowing widgets
* are added to a dropdown.
*
* @author patcoleman@google.com (Pat Coleman)
*/
public class OverflowPanelUpdater {
/**
* Required interface for anything this decorator is to control.
*/
public interface OverflowPanel {
/**
* Called at the start/end of the calculation to reset state.
* Note that other method calls should occur between these two, in particular isVisible
* and hasOverflowed are only required to be valid between calls.
*/
void onBeginOverflowLayout();
void onEndOverflowLayout();
/**
* Shows the more button. On {@link OverflowPanelUpdater#updateState} this
* will be called (if at all) before moving any items to the overflow
* bucket.
*/
void showMoreButton();
/** Moves an indexed widget into the overflow bucket. */
void moveToOverflowBucket(int index);
/** Checks whether an indexed widget on the panel would be visible (if not overflowing). */
boolean isVisible(int index);
/** Checks whether an indexed widget on the panel has overflowed. */
boolean hasOverflowed(int index);
/** Gets the number of widgets in the panel. */
int getWidgetCount();
}
/** Panel this decorates */
private final OverflowPanel panel;
/** Task for updating state of toolbar buttons */
private final Task stateUpdater = new Task() {
@Override
public void execute() {
updateState();
}
};
/**
* Create the decorator to wrap an overflow panel.
* @param panel The overflow-aware panel this decorator wraps
*/
public OverflowPanelUpdater(OverflowPanel panel) {
this.panel = panel;
}
/**
* Updates the overflow state - runs backwards through the contained widgets until a visible one
* is found on the top row, then moves everything after it into the overflow bucket.
*/
public void updateState() {
panel.onBeginOverflowLayout();
// run backwards through the items until we find one on the top line
int numChildren = panel.getWidgetCount();
int lastOnTop;
boolean itemHasOverflowed = false;
for (lastOnTop = numChildren - 1; lastOnTop >= 0; lastOnTop--) {
if (!panel.isVisible(lastOnTop)) {
continue; // skip invisible widgets
}
if (!panel.hasOverflowed(lastOnTop)) {
break; // stop once we hit widgets that haven't overflowed.
}
itemHasOverflowed = true;
}
// Show the button if any buttons have overflowed; this will change the
// layout, so continue calculating which buttons have overflowed.
if (itemHasOverflowed) {
panel.showMoreButton();
}
for (; lastOnTop >= 0; lastOnTop--) {
if (!panel.isVisible(lastOnTop)) {
continue;
}
if (!panel.hasOverflowed(lastOnTop)) {
break;
}
}
// Move all the overflowed buttons to the overflow panel.
while (++lastOnTop < numChildren) {
panel.moveToOverflowBucket(lastOnTop);
}
panel.onEndOverflowLayout();
}
/**
* Schedules a deferred call to updateState, if such a call is not already scheduled. This update
* is deferred because it is dependent on layout, which depends on StyleInjector, which defers
* things by default.
*/
public void updateStateEventually() {
if (!SchedulerInstance.get().isScheduled(stateUpdater)) {
SchedulerInstance.get().schedule(Priority.LOW, stateUpdater);
}
}
}