mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-28 14:57:12 +00:00
moved Settings interface and Component micro framework to SSE
This commit is contained in:
1 parent
c191899f3b
commit
36b963638e
102 files changed
+194
-237
No files matched your search
@@ -10,6 +10,7 @@ import com.google.gwt.i18n.client.Messages;
|
||||
public interface StringMessages extends Messages {
|
||||
String save();
|
||||
String remove();
|
||||
String ok();
|
||||
String cancel();
|
||||
String add();
|
||||
String edit();
|
||||
@@ -18,4 +19,7 @@ public interface StringMessages extends Messages {
|
||||
String remoteProcedureCall();
|
||||
String serverReplies();
|
||||
String errorCommunicatingWithServer();
|
||||
String configuration();
|
||||
String settings();
|
||||
String settingsForComponent(String localizedShortName);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.user.client.ui.LazyPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
|
||||
public abstract class AbstractLazyComponent<SettingsType extends Settings> extends LazyPanel implements LazyComponent<SettingsType> {
|
||||
|
||||
@Override
|
||||
public Widget getEntryWidget() {
|
||||
ensureWidget();
|
||||
return getWidget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDependentCssClassName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+413
@@ -0,0 +1,413 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.google.gwt.animation.client.Animation;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.dom.client.Style.Overflow;
|
||||
import com.google.gwt.dom.client.Style.Unit;
|
||||
import com.google.gwt.event.logical.shared.CloseEvent;
|
||||
import com.google.gwt.event.logical.shared.CloseHandler;
|
||||
import com.google.gwt.event.logical.shared.HasCloseHandlers;
|
||||
import com.google.gwt.event.logical.shared.HasOpenHandlers;
|
||||
import com.google.gwt.event.logical.shared.OpenEvent;
|
||||
import com.google.gwt.event.logical.shared.OpenHandler;
|
||||
import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.resources.client.ImageResource;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.HasAnimation;
|
||||
import com.google.gwt.user.client.ui.HasText;
|
||||
import com.google.gwt.user.client.ui.HasWidgets;
|
||||
import com.google.gwt.user.client.ui.Image;
|
||||
import com.google.gwt.user.client.ui.IsWidget;
|
||||
import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.google.gwt.user.client.ui.UIObject;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
|
||||
/**
|
||||
* A widget that consists of a header and a content panel that discloses the content when a user clicks on the header.
|
||||
*
|
||||
* <h3>CSS Style Rules</h3>
|
||||
* <dl class="css">
|
||||
* <dt>.collapsablePanel
|
||||
* <dd>the panel's primary style
|
||||
* <dt>.collapsablePanel-open
|
||||
* <dd>dependent style set when panel is open
|
||||
* <dt>.collapsablePanel-closed
|
||||
* <dd>dependent style set when panel is closed
|
||||
* </dl>
|
||||
* <p>
|
||||
* The header and content sections can be easily selected using css with a child selector:<br/>
|
||||
* .collapsablePanel-open .header { ... }
|
||||
* </p>
|
||||
*/
|
||||
public final class CollapsablePanel extends Composite implements HasWidgets.ForIsWidget, HasAnimation,
|
||||
HasOpenHandlers<CollapsablePanel>, HasCloseHandlers<CollapsablePanel> {
|
||||
|
||||
private static ComponentResources resources = GWT.create(ComponentResources.class);
|
||||
|
||||
/**
|
||||
* An {@link Animation} used to open the content.
|
||||
*/
|
||||
private static class ContentAnimation extends Animation {
|
||||
/**
|
||||
* Whether the item is being opened or closed.
|
||||
*/
|
||||
private boolean opening;
|
||||
|
||||
/**
|
||||
* The {@link CollapsablePanel} being affected.
|
||||
*/
|
||||
private CollapsablePanel curPanel;
|
||||
|
||||
/**
|
||||
* Open or close the content.
|
||||
*
|
||||
* @param panel
|
||||
* the panel to open or close
|
||||
* @param animate
|
||||
* true to animate, false to open instantly
|
||||
*/
|
||||
public void setOpen(CollapsablePanel panel, boolean animate) {
|
||||
// Immediately complete previous open
|
||||
cancel();
|
||||
|
||||
// Open the new item
|
||||
if (animate) {
|
||||
curPanel = panel;
|
||||
opening = panel.isOpen;
|
||||
run(ANIMATION_DURATION);
|
||||
} else {
|
||||
panel.contentWrapper.setVisible(panel.isOpen);
|
||||
if (panel.isOpen) {
|
||||
// Special treatment on the visible case to ensure LazyPanel works
|
||||
panel.getContent().setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onComplete() {
|
||||
if (!opening) {
|
||||
curPanel.contentWrapper.setVisible(false);
|
||||
}
|
||||
curPanel.contentWrapper.setHeight("auto");
|
||||
curPanel = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (opening) {
|
||||
curPanel.contentWrapper.setVisible(true);
|
||||
// Special treatment on the visible case to ensure LazyPanel works
|
||||
curPanel.getContent().setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUpdate(double progress) {
|
||||
int scrollHeight = curPanel.contentWrapper.getElement().getPropertyInt("scrollHeight");
|
||||
int height = (int) (progress * scrollHeight);
|
||||
if (!opening) {
|
||||
height = scrollHeight - height;
|
||||
}
|
||||
height = Math.max(height, 1);
|
||||
curPanel.contentWrapper.getElement().getStyle().setHeight(height, Unit.PX);
|
||||
curPanel.contentWrapper.setWidth("auto");
|
||||
}
|
||||
}
|
||||
|
||||
public interface Imager {
|
||||
Image makeImage();
|
||||
|
||||
void updateImage(boolean open, Image image);
|
||||
}
|
||||
|
||||
/**
|
||||
* The duration of the animation.
|
||||
*/
|
||||
private static final int ANIMATION_DURATION = 350;
|
||||
|
||||
// style name constants.
|
||||
private static final String STYLENAME_DEFAULT = "collapsablePanel";
|
||||
|
||||
private static final String STYLENAME_SUFFIX_OPEN = "open";
|
||||
|
||||
private static final String STYLENAME_SUFFIX_CLOSED = "closed";
|
||||
|
||||
private static final String STYLENAME_HEADER = "collapsablePanel-header";
|
||||
|
||||
private static final String STYLENAME_CONTENT = "collapsablePanel-content";
|
||||
|
||||
/**
|
||||
* The {@link Animation} used to open and close the content.
|
||||
*/
|
||||
private static ContentAnimation contentAnimation;
|
||||
|
||||
/**
|
||||
* top level widget. The first child will be a reference to {@link #header}. The second child will be a reference to
|
||||
* {@link #contentWrapper}.
|
||||
*/
|
||||
private final VerticalPanel mainPanel = new VerticalPanel();
|
||||
|
||||
/**
|
||||
* The wrapper around the content widget.
|
||||
*/
|
||||
private final SimplePanel contentWrapper = new SimplePanel();
|
||||
|
||||
/**
|
||||
* The wrapper around the header widget.
|
||||
*/
|
||||
private final SimplePanel headerWrapper = new SimplePanel();
|
||||
|
||||
private CollapsablePanelHeader headerPanel;
|
||||
|
||||
private boolean isAnimationEnabled = false;
|
||||
|
||||
private boolean isOpen = false;
|
||||
|
||||
private boolean isCollapsingEnabled = true;
|
||||
|
||||
/**
|
||||
* Creates an empty ThePanel that is initially closed.
|
||||
*/
|
||||
private CollapsablePanel() {
|
||||
initWidget(mainPanel);
|
||||
|
||||
mainPanel.add(headerWrapper);
|
||||
mainPanel.add(contentWrapper);
|
||||
contentWrapper.getElement().getStyle().setPadding(0, Unit.PX);
|
||||
contentWrapper.getElement().getStyle().setOverflow(Overflow.HIDDEN);
|
||||
setStyleName(STYLENAME_DEFAULT);
|
||||
setContentDisplay(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ThePanel with the specified header text, an initial open/close state and a bundle of images to be used
|
||||
* in the default header widget.
|
||||
*
|
||||
* @param openImage
|
||||
* the open state image resource
|
||||
* @param closedImage
|
||||
* the closed state image resource
|
||||
* @param headerText
|
||||
* the text to be displayed in the header
|
||||
*/
|
||||
public CollapsablePanel(ImageResource openImage, ImageResource closedImage, String headerText, boolean hasToolbar) {
|
||||
this();
|
||||
headerPanel = new CollapsablePanelHeader(this, openImage, closedImage, headerText, hasToolbar);
|
||||
setHeader(headerPanel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ThePanel that will be initially closed using the specified text in the header.
|
||||
*
|
||||
* @param headerText
|
||||
* the text to be displayed in the header
|
||||
*/
|
||||
public CollapsablePanel(String headerText, boolean hasToolbar) {
|
||||
this(resources.openIcon(), resources.closeIcon(), headerText, hasToolbar);
|
||||
}
|
||||
|
||||
public void setHeaderToolbar(Widget toolbar) {
|
||||
headerPanel.setToolbar(toolbar);
|
||||
}
|
||||
|
||||
public void add(Widget w) {
|
||||
if (this.getContent() == null) {
|
||||
setContent(w);
|
||||
} else {
|
||||
throw new IllegalStateException("A CollapsablePanel can only contain two Widgets.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloaded version for IsWidget.
|
||||
*
|
||||
* @see #add(Widget)
|
||||
*/
|
||||
public void add(IsWidget w) {
|
||||
this.add(asWidgetOrNull(w));
|
||||
}
|
||||
|
||||
public HandlerRegistration addCloseHandler(CloseHandler<CollapsablePanel> handler) {
|
||||
return addHandler(handler, CloseEvent.getType());
|
||||
}
|
||||
|
||||
public HandlerRegistration addOpenHandler(OpenHandler<CollapsablePanel> handler) {
|
||||
return addHandler(handler, OpenEvent.getType());
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
setContent(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the widget that was previously set in {@link #setContent(Widget)}.
|
||||
*
|
||||
* @return the panel's current content widget
|
||||
*/
|
||||
public Widget getContent() {
|
||||
return contentWrapper.getWidget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the widget that is currently being used as a header.
|
||||
*
|
||||
* @return the widget currently being used as a header
|
||||
*/
|
||||
public Widget getHeader() {
|
||||
return headerWrapper.getWidget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link HasText} instance to provide access to the headers's text, if the header widget does provide such
|
||||
* access.
|
||||
*
|
||||
* @return a reference to the header widget if it implements {@link HasText}, <code>null</code> otherwise
|
||||
*/
|
||||
public HasText getHeaderTextAccessor() {
|
||||
Widget widget = headerWrapper.getWidget();
|
||||
return (widget instanceof HasText) ? (HasText) widget : null;
|
||||
}
|
||||
|
||||
public boolean isAnimationEnabled() {
|
||||
return isAnimationEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the panel is open.
|
||||
*
|
||||
* @return <code>true</code> if panel is in open state
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
public boolean isCollapsingEnabled() {
|
||||
return isCollapsingEnabled;
|
||||
}
|
||||
|
||||
public void setCollapsingEnabled(boolean enable) {
|
||||
isCollapsingEnabled = enable;
|
||||
|
||||
this.headerPanel.setCollapsingEnabled(enable);
|
||||
}
|
||||
|
||||
public Iterator<Widget> iterator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean remove(Widget w) {
|
||||
if (w == getContent()) {
|
||||
setContent(null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean remove(IsWidget w) {
|
||||
return this.remove(asWidgetOrNull(w));
|
||||
}
|
||||
|
||||
public void setAnimationEnabled(boolean enable) {
|
||||
isAnimationEnabled = enable;
|
||||
}
|
||||
|
||||
public void setHeader(Widget headerContent) {
|
||||
final Widget currentHeaderContent = getHeader();
|
||||
|
||||
// Remove existing header content widget.
|
||||
if (currentHeaderContent != null) {
|
||||
headerWrapper.setWidget(null);
|
||||
currentHeaderContent.removeStyleName(STYLENAME_HEADER);
|
||||
}
|
||||
|
||||
// Add new header content widget if != null.
|
||||
if (headerContent != null) {
|
||||
headerWrapper.setWidget(headerContent);
|
||||
|
||||
headerContent.addStyleName(STYLENAME_HEADER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content widget which can be opened and closed by this panel. If there is a preexisting content widget,
|
||||
* it will be detached.
|
||||
*
|
||||
* @param content
|
||||
* the widget to be used as the content panel
|
||||
*/
|
||||
public void setContent(Widget content) {
|
||||
final Widget currentContent = getContent();
|
||||
|
||||
// Remove existing content widget.
|
||||
if (currentContent != null) {
|
||||
contentWrapper.setWidget(null);
|
||||
currentContent.removeStyleName(STYLENAME_CONTENT);
|
||||
}
|
||||
|
||||
// Add new content widget if != null.
|
||||
if (content != null) {
|
||||
contentWrapper.setWidget(content);
|
||||
|
||||
content.addStyleName(STYLENAME_CONTENT);
|
||||
setContentDisplay(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the visible state of this <code>ThePanel</code>.
|
||||
*
|
||||
* @param isOpen
|
||||
* <code>true</code> to open the panel, <code>false</code> to close
|
||||
*/
|
||||
public void setOpen(boolean isOpen) {
|
||||
if (this.isOpen != isOpen) {
|
||||
this.isOpen = isOpen;
|
||||
setContentDisplay(true);
|
||||
fireEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Affected Elements:</b>
|
||||
* <ul>
|
||||
* <li>-header = the clickable header.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see UIObject#onEnsureDebugId(String)
|
||||
*/
|
||||
@Override
|
||||
protected void onEnsureDebugId(String baseID) {
|
||||
super.onEnsureDebugId(baseID);
|
||||
headerWrapper.ensureDebugId(baseID + "-header");
|
||||
}
|
||||
|
||||
private void fireEvent() {
|
||||
if (isOpen) {
|
||||
OpenEvent.fire(this, this);
|
||||
} else {
|
||||
CloseEvent.fire(this, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void setContentDisplay(boolean animate) {
|
||||
if (isOpen) {
|
||||
removeStyleDependentName(STYLENAME_SUFFIX_CLOSED);
|
||||
addStyleDependentName(STYLENAME_SUFFIX_OPEN);
|
||||
} else {
|
||||
removeStyleDependentName(STYLENAME_SUFFIX_OPEN);
|
||||
addStyleDependentName(STYLENAME_SUFFIX_CLOSED);
|
||||
}
|
||||
|
||||
if (getContent() != null) {
|
||||
if (contentAnimation == null) {
|
||||
contentAnimation = new ContentAnimation();
|
||||
}
|
||||
contentAnimation.setOpen(this, animate && isAnimationEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.event.logical.shared.CloseEvent;
|
||||
import com.google.gwt.event.logical.shared.CloseHandler;
|
||||
import com.google.gwt.event.logical.shared.OpenEvent;
|
||||
import com.google.gwt.event.logical.shared.OpenHandler;
|
||||
import com.google.gwt.resources.client.ImageResource;
|
||||
import com.google.gwt.user.client.DOM;
|
||||
import com.google.gwt.user.client.Event;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.HasText;
|
||||
import com.google.gwt.user.client.ui.Image;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.gwt.client.shared.components.CollapsablePanel.Imager;
|
||||
|
||||
/**
|
||||
* The default header widget used within a {@link CollapsablePanel}.
|
||||
*/
|
||||
public class CollapsablePanelHeader extends FlowPanel implements HasText, OpenHandler<CollapsablePanel>,
|
||||
CloseHandler<CollapsablePanel> {
|
||||
|
||||
private final Label textLabel;
|
||||
private final Image iconImage;
|
||||
private final Imager imager;
|
||||
private final CollapsablePanel collapsablePanel;
|
||||
private final boolean hasToolbar;
|
||||
private final SimplePanel toolbarPanel;
|
||||
private final ClickableHeader clickableHeaderPanel;
|
||||
|
||||
private static final String STYLENAME_TOOLBAR = "collapsablePanel-toolbar";
|
||||
|
||||
private static final String STYLENAME_HEADER_COLLAPSE_AREA = "collapsablePanel-header-collapse";
|
||||
|
||||
private static final String STYLENAME_HEADER_TEXT = "collapsablePanel-header-text";
|
||||
|
||||
private static final String STYLENAME_HEADER_IMAGE = "collapsablePanel-header-image";
|
||||
|
||||
protected CollapsablePanelHeader(CollapsablePanel collapsablePanel, Imager imager, String text, boolean hasToolbar) {
|
||||
this.imager = imager;
|
||||
this.hasToolbar = hasToolbar;
|
||||
this.collapsablePanel = collapsablePanel;
|
||||
iconImage = imager.makeImage();
|
||||
iconImage.addStyleName(STYLENAME_HEADER_IMAGE);
|
||||
textLabel= new Label(text);
|
||||
textLabel.addStyleName(STYLENAME_HEADER_TEXT);
|
||||
|
||||
clickableHeaderPanel = new ClickableHeader(collapsablePanel);
|
||||
clickableHeaderPanel.setStyleName(STYLENAME_HEADER_COLLAPSE_AREA);
|
||||
|
||||
add(clickableHeaderPanel);
|
||||
|
||||
FlowPanel clickableInnerPanel = new FlowPanel();
|
||||
clickableHeaderPanel.setWidget(clickableInnerPanel);
|
||||
|
||||
clickableInnerPanel.add(iconImage);
|
||||
clickableInnerPanel.add(textLabel);
|
||||
|
||||
if(hasToolbar) {
|
||||
toolbarPanel = new SimplePanel();
|
||||
add(toolbarPanel);
|
||||
} else {
|
||||
toolbarPanel = null;
|
||||
}
|
||||
|
||||
collapsablePanel.addOpenHandler(this);
|
||||
collapsablePanel.addCloseHandler(this);
|
||||
setStyle();
|
||||
}
|
||||
|
||||
protected CollapsablePanelHeader(CollapsablePanel collapsablePanel, final ImageResource openImage,
|
||||
final ImageResource closedImage, String text, boolean hasToolbar) {
|
||||
this(collapsablePanel, new Imager() {
|
||||
public Image makeImage() {
|
||||
return new Image(closedImage);
|
||||
}
|
||||
|
||||
public void updateImage(boolean open, Image image) {
|
||||
if (open) {
|
||||
image.setResource(openImage);
|
||||
} else {
|
||||
image.setResource(closedImage);
|
||||
}
|
||||
}
|
||||
}, text, hasToolbar);
|
||||
}
|
||||
|
||||
public void setToolbar(Widget toolbar) {
|
||||
if (hasToolbar && toolbarPanel != null) {
|
||||
toolbarPanel.setWidget(toolbar);
|
||||
toolbarPanel.setStyleName(STYLENAME_TOOLBAR);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setCollapsingEnabled(boolean enabled)
|
||||
{
|
||||
clickableHeaderPanel.setClickable(enabled);
|
||||
}
|
||||
|
||||
public final String getText() {
|
||||
return textLabel.getText();
|
||||
}
|
||||
|
||||
public final void onClose(CloseEvent<CollapsablePanel> event) {
|
||||
setStyle();
|
||||
}
|
||||
|
||||
public final void onOpen(OpenEvent<CollapsablePanel> event) {
|
||||
setStyle();
|
||||
}
|
||||
|
||||
public final void setText(String text) {
|
||||
textLabel.setText(text);
|
||||
}
|
||||
|
||||
private void setStyle() {
|
||||
imager.updateImage(collapsablePanel.isOpen(), iconImage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to wrap widgets in the header to provide click support. Effectively wraps the widget in an
|
||||
* <code>anchor</code> to get automatic keyboard access.
|
||||
*/
|
||||
private final class ClickableHeader extends SimplePanel {
|
||||
private final CollapsablePanel panel;
|
||||
|
||||
private boolean isClickable = true;
|
||||
|
||||
private ClickableHeader(final CollapsablePanel panel) {
|
||||
// Anchor is used to allow keyboard access.
|
||||
super(DOM.createAnchor());
|
||||
this.panel = panel;
|
||||
Element elem = getElement();
|
||||
elem.setPropertyString("href", "javascript:void(0);");
|
||||
// Avoids layout problems from having blocks in inlines.
|
||||
//DOM.setStyleAttribute(elem, "display", "block");
|
||||
sinkEvents(Event.ONCLICK);
|
||||
}
|
||||
|
||||
protected void setClickable(boolean enabled) {
|
||||
this.isClickable = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBrowserEvent(Event event) {
|
||||
// no need to call super.
|
||||
switch (DOM.eventGetType(event)) {
|
||||
case Event.ONCLICK:
|
||||
// Prevent link default action.
|
||||
event.preventDefault();
|
||||
if(isClickable) {
|
||||
panel.setOpen(!panel.isOpen());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
|
||||
public interface Component<SettingsType extends Settings> {
|
||||
/**
|
||||
* @return the name to display to a user for quick navigation to this component
|
||||
*/
|
||||
String getLocalizedShortName();
|
||||
|
||||
/**
|
||||
* A component may or may not have a {@link Widget} used to render its contents. Typically,
|
||||
* entries with sub-entries don't afford a widget of their own but let the framework choose a container
|
||||
* in which to aggregate their entries' widgets.
|
||||
*/
|
||||
Widget getEntryWidget();
|
||||
|
||||
boolean isVisible();
|
||||
|
||||
void setVisible(boolean visibility);
|
||||
|
||||
/**
|
||||
* @return whether this component has settings that a user may change; if so,
|
||||
*/
|
||||
boolean hasSettings();
|
||||
|
||||
/**
|
||||
* If this component {@link #hasSettings has settings}, this method may return a component for the settings dialog.
|
||||
* It will be used to obtain a widget shown in the settings dialog, a validator for the component-specific settings,
|
||||
* as well as to produce a result from the widget's state when the settings dialog wants to validate or return the
|
||||
* settings.
|
||||
*/
|
||||
SettingsDialogComponent<SettingsType> getSettingsDialogComponent();
|
||||
|
||||
/**
|
||||
* Updates the settings of this component. Expected to be called when a settings dialog using this component's
|
||||
* {@link #getSettingsDialogComponent()} has been confirmed.
|
||||
*/
|
||||
void updateSettings(SettingsType newSettings);
|
||||
|
||||
/**
|
||||
* If the component wants to add its specific styling rules, e.g., for the expand/collapse toggle buttons or
|
||||
* the dragger handle, it should return a non-<code>null</code> value from this method which is then used as
|
||||
* a dependent CSS class name for those components.
|
||||
*/
|
||||
String getDependentCssClassName();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
|
||||
public interface ComponentGroup<SettingsType extends Settings> extends Component<SettingsType> {
|
||||
|
||||
boolean addComponent(Component<?> component);
|
||||
|
||||
boolean removeComponent(Component<?> component);
|
||||
|
||||
Iterable<Component<?>> getComponents();
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.resources.client.ClientBundle;
|
||||
import com.google.gwt.resources.client.ImageResource;
|
||||
|
||||
public interface ComponentResources extends ClientBundle {
|
||||
|
||||
@Source("com/sap/sse/gwt/client/images/open.png")
|
||||
ImageResource openIcon();
|
||||
|
||||
@Source("com/sap/sse/gwt/client/images/close.png")
|
||||
ImageResource closeIcon();
|
||||
|
||||
@Source("com/sap/sse/gwt/client/images/settings.png")
|
||||
ImageResource settingsIcon();
|
||||
|
||||
@Source("com/sap/sse/gwt/client/images/leaderboardsettings.png")
|
||||
ImageResource darkSettingsIcon();
|
||||
}
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.resources.client.ImageResource;
|
||||
import com.google.gwt.user.client.ui.AbstractImagePrototype;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.HorizontalPanel;
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
import com.sap.sse.gwt.client.StringMessages;
|
||||
|
||||
/**
|
||||
* One entry in a component toolbar. Represents one {@link Component} that can be selected and whose
|
||||
* settings editing dialog can be reached from here.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
* @param <SettingsType>
|
||||
*/
|
||||
public class ComponentToolbarEntry<SettingsType extends Settings> extends HorizontalPanel {
|
||||
private static ComponentResources resources = GWT.create(ComponentResources.class);
|
||||
|
||||
private final Component<SettingsType> component;
|
||||
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
public ComponentToolbarEntry(final Component<SettingsType> component, final StringMessages stringMessages) {
|
||||
super();
|
||||
this.setSpacing(10);
|
||||
this.component = component;
|
||||
this.stringMessages = stringMessages;
|
||||
}
|
||||
|
||||
public void addSettingsButton() {
|
||||
if (component.hasSettings()) {
|
||||
ImageResource settingsImage = resources.darkSettingsIcon();
|
||||
Anchor showConfigAnchor = new Anchor(AbstractImagePrototype.create(settingsImage).getSafeHtml());
|
||||
showConfigAnchor.setTitle(stringMessages.configuration());
|
||||
showConfigAnchor.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
new SettingsDialog<SettingsType>(component, stringMessages).show();
|
||||
}
|
||||
});
|
||||
this.add(showConfigAnchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
|
||||
public interface ComponentViewer {
|
||||
|
||||
Widget getViewerWidget();
|
||||
|
||||
String getViewerName();
|
||||
|
||||
Component<?> getRootComponent();
|
||||
|
||||
void forceLayout();
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
|
||||
public class CompositeSettings implements Settings {
|
||||
public static class ComponentAndSettingsPair<SettingsType extends Settings> extends Util.Pair<Component<SettingsType>, SettingsType> {
|
||||
private static final long serialVersionUID = -569811233041583043L;
|
||||
|
||||
public ComponentAndSettingsPair(Component<SettingsType> a, SettingsType b) {
|
||||
super(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
private final Iterable<ComponentAndSettingsPair<?>> settingsPerComponent;
|
||||
|
||||
public CompositeSettings(Iterable<ComponentAndSettingsPair<?>> settingsPerComponent) {
|
||||
this.settingsPerComponent = settingsPerComponent;
|
||||
}
|
||||
|
||||
public Iterable<ComponentAndSettingsPair<?>> getSettingsPerComponent() {
|
||||
return settingsPerComponent;
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.TabPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator;
|
||||
|
||||
public class CompositeSettingsDialogComponent implements SettingsDialogComponent<CompositeSettings> {
|
||||
|
||||
private final Component<?>[] components;
|
||||
|
||||
public CompositeSettingsDialogComponent(Component<?>... components) {
|
||||
this.components = components;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Widget getAdditionalWidget(DataEntryDialog<?> dialog) {
|
||||
TabPanel result = new TabPanel();
|
||||
for (Component<?> component : components) {
|
||||
Widget w = component.getSettingsDialogComponent().getAdditionalWidget((DataEntryDialog<?>) dialog);
|
||||
result.add(w, component.getLocalizedShortName());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompositeSettings getResult() {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Validator<CompositeSettings> getValidator() {
|
||||
return new CompositeValidator(components);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FocusWidget getFocusWidget() {
|
||||
for (Component<?> component : components) {
|
||||
FocusWidget fw = component.getSettingsDialogComponent().getFocusWidget();
|
||||
if (fw != null) {
|
||||
return fw;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/shared/components/CompositeTabbedSettingsDialog.java
Executable
+92
@@ -0,0 +1,92 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
import com.sap.sse.gwt.client.StringMessages;
|
||||
import com.sap.sse.gwt.client.shared.components.CompositeSettings.ComponentAndSettingsPair;
|
||||
|
||||
/**
|
||||
* A composite settings dialog that combines the settings of several {@link Component}s, providing a tab
|
||||
* for each component.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class CompositeTabbedSettingsDialog extends SettingsDialog<CompositeSettings> {
|
||||
private final Map<Component<?>, SettingsDialogComponent<?>> settingsDialogComponents;
|
||||
|
||||
public CompositeTabbedSettingsDialog(StringMessages stringConstants, final Component<?>... components) {
|
||||
super(new Component<CompositeSettings>() {
|
||||
@Override
|
||||
public boolean hasSettings() {
|
||||
for (Component<?> component : components) {
|
||||
if (component.hasSettings()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingsDialogComponent<CompositeSettings> getSettingsDialogComponent() {
|
||||
return new CompositeSettingsDialogComponent(components);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSettings(CompositeSettings newSettings) {
|
||||
for (CompositeSettings.ComponentAndSettingsPair<?> componentAndSettings : newSettings.getSettingsPerComponent()) {
|
||||
updateSettings(componentAndSettings);
|
||||
}
|
||||
}
|
||||
|
||||
private <SettingsType extends Settings> void updateSettings(ComponentAndSettingsPair<SettingsType> componentAndSettings) {
|
||||
componentAndSettings.getA().updateSettings(componentAndSettings.getB());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalizedShortName() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (Component<?> component : components) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
result.append(" / ");
|
||||
}
|
||||
result.append(component.getLocalizedShortName());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Widget getEntryWidget() {
|
||||
throw new RuntimeException("Virtual composite component doesn't have a widget of its own");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visibility) {
|
||||
throw new RuntimeException("Virtual composite component doesn't know how to make itself visible");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDependentCssClassName() {
|
||||
return null;
|
||||
}
|
||||
}, stringConstants);
|
||||
settingsDialogComponents = new HashMap<Component<?>, SettingsDialogComponent<?>>();
|
||||
for (Component<?> component : components) {
|
||||
if (component.hasSettings()) {
|
||||
settingsDialogComponents.put(component, component.getSettingsDialogComponent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator;
|
||||
import com.sap.sse.gwt.client.shared.components.CompositeSettings.ComponentAndSettingsPair;
|
||||
|
||||
public class CompositeValidator implements Validator<CompositeSettings> {
|
||||
private static class ComponentAndValidator<SettingsType extends Settings> extends Util.Pair<Component<SettingsType>, Validator<SettingsType>> {
|
||||
private static final long serialVersionUID = -4190322565836849861L;
|
||||
|
||||
public ComponentAndValidator(Component<SettingsType> a, Validator<SettingsType> b) {
|
||||
super(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
private final Iterable<ComponentAndValidator<?>> validators;
|
||||
|
||||
public CompositeValidator(Component<?>[] components) {
|
||||
ArrayList<ComponentAndValidator<?>> v = new ArrayList<ComponentAndValidator<?>>();
|
||||
for (Component<?> component : components) {
|
||||
v.add(getComponentAndValidator(component));
|
||||
}
|
||||
validators = v;
|
||||
}
|
||||
|
||||
private <SettingsType extends Settings> ComponentAndValidator<SettingsType> getComponentAndValidator(Component<SettingsType> component) {
|
||||
return new ComponentAndValidator<SettingsType>(component, component.getSettingsDialogComponent().getValidator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage(CompositeSettings valueToValidate) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (ComponentAndSettingsPair<?> componentAndSettings : valueToValidate.getSettingsPerComponent()) {
|
||||
final String errorMessage = getErrorMessage(componentAndSettings);
|
||||
if (errorMessage != null) {
|
||||
result.append(errorMessage);
|
||||
result.append("; ");
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private <SettingsType extends Settings> String getErrorMessage(ComponentAndSettingsPair<SettingsType> componentAndSettings) {
|
||||
for (ComponentAndValidator<?> componentAndValidator : validators) {
|
||||
if (componentAndValidator.getA() == componentAndSettings.getA()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Validator<SettingsType> validator = (Validator<SettingsType>) componentAndValidator.getB();
|
||||
return validator.getErrorMessage(componentAndSettings.getB());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
|
||||
|
||||
public interface IsEmbeddableComponent {
|
||||
|
||||
Widget getHeaderWidget();
|
||||
|
||||
Widget getContentWidget();
|
||||
|
||||
Widget getToolbarWidget();
|
||||
|
||||
Widget getLegendWidget();
|
||||
|
||||
BusyIndicator getBusyIndicator();
|
||||
|
||||
boolean isEmbedded();
|
||||
|
||||
boolean hasToolbar();
|
||||
|
||||
boolean hasBusyIndicator();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
|
||||
public interface LazyComponent<SettingsType extends Settings> extends Component<SettingsType> {
|
||||
Widget createWidget();
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
import com.sap.sse.gwt.client.StringMessages;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class SettingsDialog<SettingsType extends Settings> extends DataEntryDialog<SettingsType> {
|
||||
private final SettingsDialogComponent<SettingsType> settingsDialogComponent;
|
||||
|
||||
public SettingsDialog(final Component<SettingsType> component, StringMessages stringMessages) {
|
||||
this(component, stringMessages, /* animationEnabled */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a settings button for <code>component</code> that, when clicked, opens a settings dialog for that
|
||||
* component and when confirmed, updates that component's settings. The button has no CSS style attached to
|
||||
* give callers full flexibility as to how to style the button.
|
||||
*/
|
||||
public static <T extends Settings> Button createSettingsButton(final Component<T> component, final StringMessages stringMessages) {
|
||||
Button settingsButton = new Button();
|
||||
settingsButton.setTitle(stringMessages.settings());
|
||||
settingsButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
new SettingsDialog<T>(component, stringMessages).show();
|
||||
}
|
||||
});
|
||||
return settingsButton;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This auxiliary constructor is required to avoid duplicate calls to {@link Component#getSettingsDialogComponent()}
|
||||
* which may choose to create a new instance each call. Such duplicate instances would cause the validator to
|
||||
* operate on a different instance as the one used for displaying, hence not allowing the validator to use the UI
|
||||
* elements, neither for update nor read.
|
||||
*/
|
||||
private SettingsDialog(final Component<SettingsType> component,
|
||||
SettingsDialogComponent<SettingsType> dialogComponent, StringMessages stringMessages,
|
||||
boolean animationEnabled) {
|
||||
super(stringMessages.settingsForComponent(component.getLocalizedShortName()), null, stringMessages.ok(),
|
||||
stringMessages.cancel(), dialogComponent.getValidator(), animationEnabled,
|
||||
new DialogCallback<SettingsType>() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ok(SettingsType newSettings) {
|
||||
component.updateSettings(newSettings);
|
||||
}
|
||||
});
|
||||
this.settingsDialogComponent = dialogComponent;
|
||||
}
|
||||
|
||||
public SettingsDialog(final Component<SettingsType> component, StringMessages stringMessages, boolean animationEnabled) {
|
||||
this(component, component.getSettingsDialogComponent(), stringMessages, animationEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
return settingsDialogComponent.getAdditionalWidget(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SettingsType getResult() {
|
||||
return settingsDialogComponent.getResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
super.show();
|
||||
FocusWidget focusWidget = settingsDialogComponent.getFocusWidget();
|
||||
if (focusWidget != null) {
|
||||
focusWidget.setFocus(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
package com.sap.sse.gwt.client.shared.components;
|
||||
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator;
|
||||
|
||||
public interface SettingsDialogComponent<SettingsType> {
|
||||
/**
|
||||
* Called by the framework to obtain the widget to show in the settings dialog. Called each time before the
|
||||
* settings dialog is shown, therefore also before {@link #getResult()} is called and before any method on
|
||||
* the validator returned by {@link #getValidator()} is called.
|
||||
*
|
||||
* @param dialog
|
||||
* can be used to {@link DataEntryDialog#createCheckbox(String) produce a checkbox}, a
|
||||
* {@link DataEntryDialog#createTextBox(String)} or other data entry elements that participate in key
|
||||
* handling for validation triggering and for canceling and confirming the dialog. The type argument
|
||||
* is therefore not propagated. It's not needed to create a simple UI control.
|
||||
* @return <code>null</code> in case this component doesn't make any special contribution to the settings dialog, or
|
||||
* a valid widget that will be displayed in the settings dialog
|
||||
*/
|
||||
Widget getAdditionalWidget(DataEntryDialog<?> dialog);
|
||||
|
||||
/**
|
||||
* Obtains the result from this settings dialog component; usually, the result object is contructed by reading data
|
||||
* from the elements of the widget constructed by {@link #getAdditionalWidget(DataEntryDialog)}.
|
||||
*/
|
||||
SettingsType getResult();
|
||||
|
||||
/**
|
||||
* Obtains an optional validator that is used to validate the {@link #getAdditionalWidget(DataEntryDialog)} contents upon
|
||||
* changes. If <code>null</code> is returned, no validation will be performed.
|
||||
*/
|
||||
Validator<SettingsType> getValidator();
|
||||
|
||||
/**
|
||||
* @return <code>null</code> if no initial focus on any widget is desired, or a widget from the
|
||||
* {@link #getAdditionalWidget(DataEntryDialog)} result that will then receive the focus when the settings
|
||||
* dialog is shown
|
||||
*/
|
||||
FocusWidget getFocusWidget();
|
||||
}
|
||||
Reference in new issue
Block a user