IDEMPIERE-231 Zk6: Improve the tablet experience. Fixed compatibility with Chrome for Android ( Tested on Nexus 7 4.2.2 with Chrome 25 ). Added swipe gesture support - swipe left to hide the left desktop column, swipe right to hide the right desktop column ( tooltips ), swipe down to hide the detail tabs and swipe left/right on the tab header to close a tab. Hide the login info for mobile devices.

This commit is contained in:
Heng Sin Low 2013-03-01 15:29:45 +08:00
parent e0279e57d4
commit ad42f1bf83
8 changed files with 138 additions and 36 deletions

View File

@ -449,14 +449,8 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
appDesktop.setClientInfo(clientInfo); appDesktop.setClientInfo(clientInfo);
String ua = Servlets.getUserAgent((ServletRequest) Executions.getCurrent().getNativeRequest()); String ua = Servlets.getUserAgent((ServletRequest) Executions.getCurrent().getNativeRequest());
clientInfo.userAgent = ua; clientInfo.userAgent = ua;
if (Servlets.getBrowser(ua).equals("webkit")) { ua = ua.toLowerCase();
ua = ua.toLowerCase(); clientInfo.tablet = ua.indexOf("ipad") >= 0 || ua.indexOf("iphone") >= 0 || ua.indexOf("android") >= 0;
if (ua.indexOf("ipad") >= 0) {
clientInfo.tablet = true;
} else if (ua.indexOf("android") >= 0 && ua.indexOf("chrome") >= 0 && ua.indexOf("mobile") < 0) {
clientInfo.tablet = true;
}
}
if (getDesktop() != null && getDesktop().getSession() != null) { if (getDesktop() != null && getDesktop().getSession() != null) {
getDesktop().getSession().setAttribute(CLIENT_INFO, clientInfo); getDesktop().getSession().setAttribute(CLIENT_INFO, clientInfo);
} }

View File

@ -17,11 +17,14 @@ package org.adempiere.webui;
import java.util.Locale; import java.util.Locale;
import java.util.Properties; import java.util.Properties;
import javax.servlet.ServletRequest;
import org.adempiere.webui.apps.AEnv; import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.part.AbstractUIPart; import org.adempiere.webui.part.AbstractUIPart;
import org.adempiere.webui.theme.ITheme; import org.adempiere.webui.theme.ITheme;
import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.theme.ThemeManager;
import org.adempiere.webui.window.LoginWindow; import org.adempiere.webui.window.LoginWindow;
import org.zkoss.web.servlet.Servlets;
import org.zkoss.zhtml.Text; import org.zkoss.zhtml.Text;
import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.Executions;
@ -123,6 +126,13 @@ public class WLogin extends AbstractUIPart
West west = new West(); West west = new West();
west.setSclass(ITheme.LOGIN_WEST_PANEL_CLASS); west.setSclass(ITheme.LOGIN_WEST_PANEL_CLASS);
addContent(west, pageDefintion); addContent(west, pageDefintion);
String ua = Servlets.getUserAgent((ServletRequest) Executions.getCurrent().getNativeRequest());
ua = ua.toLowerCase();
boolean mobile = ua.indexOf("ipad") >= 0 || ua.indexOf("iphone") >= 0 || ua.indexOf("android") >= 0;
if (mobile) {
west.setCollapsible(true);
west.setOpen(false);
}
} catch (Exception e){ } catch (Exception e){
//ignore page not found exception //ignore page not found exception
if (e instanceof UiException) { if (e instanceof UiException) {

View File

@ -83,6 +83,7 @@ import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.OpenEvent; import org.zkoss.zk.ui.event.OpenEvent;
import org.zkoss.zk.ui.event.SwipeEvent;
import org.zkoss.zk.ui.util.Clients; import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Button; import org.zkoss.zul.Button;
import org.zkoss.zul.Cell; import org.zkoss.zul.Cell;
@ -186,6 +187,14 @@ DataStatusListener, IADTabpanel, IdSpace
public static final String ON_TOGGLE_EVENT = "onToggle"; public static final String ON_TOGGLE_EVENT = "onToggle";
private static enum SouthEvent {
SLIDE(),
OPEN(),
CLOSE();
private SouthEvent() {}
}
public ADTabpanel() public ADTabpanel()
{ {
init(); init();
@ -238,6 +247,21 @@ DataStatusListener, IADTabpanel, IdSpace
"var se = new zk.Event(this, 'onSlide', null, {toServer: true}); zAu.send(se); } }"); "var se = new zk.Event(this, 'onSlide', null, {toServer: true}); zAu.send(se); } }");
south.addEventListener(Events.ON_OPEN, this); south.addEventListener(Events.ON_OPEN, this);
south.addEventListener("onSlide", this); south.addEventListener("onSlide", this);
south.addEventListener(Events.ON_SWIPE, new EventListener<SwipeEvent>() {
@Override
public void onEvent(SwipeEvent event) throws Exception {
if ("down".equals(event.getSwipeDirection())) {
Borderlayout borderLayout = (Borderlayout) formContainer;
South south = borderLayout.getSouth();
if (south.isOpen()) {
south.setOpen(false);
onSouthEvent(SouthEvent.CLOSE);
}
}
}
});
} }
south.appendChild(component); south.appendChild(component);
@ -973,23 +997,11 @@ DataStatusListener, IADTabpanel, IdSpace
if (detailPane != null) { if (detailPane != null) {
boolean openEvent = event instanceof OpenEvent; boolean openEvent = event instanceof OpenEvent;
if (openEvent) { if (openEvent) {
Events.echoEvent(ON_SAVE_OPEN_PREFERENCE_EVENT, this, ((OpenEvent)event).isOpen()); OpenEvent oe = (OpenEvent)event;
if (!((OpenEvent)event).isOpen()) { onSouthEvent(oe.isOpen() ? SouthEvent.OPEN : SouthEvent.CLOSE);
return; } else {
} onSouthEvent(SouthEvent.SLIDE);
} }
if (detailPane.getParent() == null) {
formContainer.appendSouth(detailPane);
}
IADTabpanel tabPanel = detailPane.getSelectedADTabpanel();
if (tabPanel != null) {
if (!tabPanel.isActivated()) {
tabPanel.activate(true);
}
if (!tabPanel.isGridView()) {
tabPanel.switchRowPresentation();
}
}
} }
} }
else if (event.getName().equals(ON_SAVE_OPEN_PREFERENCE_EVENT)) { else if (event.getName().equals(ON_SAVE_OPEN_PREFERENCE_EVENT)) {
@ -1037,6 +1049,28 @@ DataStatusListener, IADTabpanel, IdSpace
} }
} }
private void onSouthEvent(SouthEvent event) {
if (event == SouthEvent.OPEN || event == SouthEvent.CLOSE) {
boolean open = event == SouthEvent.OPEN ? true : false;
Events.echoEvent(ON_SAVE_OPEN_PREFERENCE_EVENT, this, open);
if (!open)
return;
}
if (detailPane.getParent() == null) {
formContainer.appendSouth(detailPane);
}
IADTabpanel tabPanel = detailPane.getSelectedADTabpanel();
if (tabPanel != null) {
if (!tabPanel.isActivated()) {
tabPanel.activate(true);
}
if (!tabPanel.isGridView()) {
tabPanel.switchRowPresentation();
}
}
}
private boolean isOpenDetailPane() { private boolean isOpenDetailPane() {
boolean open = true; boolean open = true;
int windowId = getGridTab().getAD_Window_ID(); int windowId = getGridTab().getAD_Window_ID();

View File

@ -374,9 +374,14 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace
Columns columns = new Columns(); Columns columns = new Columns();
Frozen frozen = new Frozen(); //frozen not working well on tablet devices yet
frozen.setColumns(1); if (!AEnv.isTablet())
listbox.appendChild(frozen); {
Frozen frozen = new Frozen();
frozen.setColumns(1);
listbox.appendChild(frozen);
}
org.zkoss.zul.Column indicator = new Column(); org.zkoss.zul.Column indicator = new Column();
indicator.setWidth("18px"); indicator.setWidth("18px");
try { try {

View File

@ -73,6 +73,7 @@ import org.zkoss.zk.ui.event.EventQueue;
import org.zkoss.zk.ui.event.EventQueues; import org.zkoss.zk.ui.event.EventQueues;
import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.OpenEvent; import org.zkoss.zk.ui.event.OpenEvent;
import org.zkoss.zk.ui.event.SwipeEvent;
import org.zkoss.zk.ui.util.Clients; import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zk.ui.util.DesktopCleanup; import org.zkoss.zk.ui.util.DesktopCleanup;
import org.zkoss.zul.Borderlayout; import org.zkoss.zul.Borderlayout;
@ -177,9 +178,20 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
@Override @Override
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
OpenEvent oe = (OpenEvent) event; OpenEvent oe = (OpenEvent) event;
UserPreference pref = SessionManager.getSessionApplication().getUserPreference(); updateMenuCollapsedPreference(!oe.isOpen());
pref.setProperty(UserPreference.P_MENU_COLLAPSED, !oe.isOpen()); }
pref.savePreference(); });
w.addEventListener(Events.ON_SWIPE, new EventListener<SwipeEvent>() {
@Override
public void onEvent(SwipeEvent event) throws Exception {
if ("left".equals(event.getSwipeDirection())) {
West w = (West) event.getTarget();
if (w.isOpen()) {
w.setOpen(false);
updateMenuCollapsedPreference(true);
}
}
} }
}); });
UserPreference pref = SessionManager.getSessionApplication().getUserPreference(); UserPreference pref = SessionManager.getSessionApplication().getUserPreference();
@ -199,11 +211,24 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
@Override @Override
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
OpenEvent oe = (OpenEvent) event; OpenEvent oe = (OpenEvent) event;
UserPreference pref = SessionManager.getSessionApplication().getUserPreference(); updateHelpCollapsedPreference(!oe.isOpen());
pref.setProperty(UserPreference.P_HELP_COLLAPSED, !oe.isOpen());
pref.savePreference();
} }
}); });
e.addEventListener(Events.ON_SWIPE, new EventListener<SwipeEvent>() {
@Override
public void onEvent(SwipeEvent event) throws Exception {
if ("right".equals(event.getSwipeDirection())) {
East e = (East) event.getTarget();
if (e.isOpen()) {
e.setOpen(false);
updateHelpCollapsedPreference(true);
}
}
}
});
boolean helpCollapsed= pref.isPropertyBool(UserPreference.P_HELP_COLLAPSED); boolean helpCollapsed= pref.isPropertyBool(UserPreference.P_HELP_COLLAPSED);
e.setOpen(!helpCollapsed); e.setOpen(!helpCollapsed);
@ -258,6 +283,18 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
return layout; return layout;
} }
private void updateMenuCollapsedPreference(boolean collapsed) {
UserPreference pref = SessionManager.getSessionApplication().getUserPreference();
pref.setProperty(UserPreference.P_MENU_COLLAPSED, collapsed);
pref.savePreference();
}
private void updateHelpCollapsedPreference(boolean collapsed) {
UserPreference pref = SessionManager.getSessionApplication().getUserPreference();
pref.setProperty(UserPreference.P_HELP_COLLAPSED, collapsed);
pref.savePreference();
}
private void renderHomeTab() private void renderHomeTab()
{ {
homeTab.getChildren().clear(); homeTab.getChildren().clear();

View File

@ -39,6 +39,7 @@ import org.compiere.util.Util;
import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.SwipeEvent;
import org.zkoss.zul.Center; import org.zkoss.zul.Center;
import org.zkoss.zul.South; import org.zkoss.zul.South;
@ -302,6 +303,15 @@ public class InfoProductWindow extends InfoWindow {
south.setSplittable(true); south.setSplittable(true);
south.setTitle(Msg.translate(Env.getCtx(), "WarehouseStock")); south.setTitle(Msg.translate(Env.getCtx(), "WarehouseStock"));
south.setTooltiptext(Msg.translate(Env.getCtx(), "WarehouseStock")); south.setTooltiptext(Msg.translate(Env.getCtx(), "WarehouseStock"));
south.addEventListener(Events.ON_SWIPE, new EventListener<SwipeEvent>() {
@Override
public void onEvent(SwipeEvent event) throws Exception {
South south = (South) event.getTarget();
if ("down".equals(event.getSwipeDirection())) {
south.setOpen(false);
}
}
});
contentBorderLayout.appendChild(south); contentBorderLayout.appendChild(south);
tabbedPane.setSclass("info-product-tabbedpane"); tabbedPane.setSclass("info-product-tabbedpane");
south.appendChild(tabbedPane); south.appendChild(tabbedPane);

View File

@ -29,6 +29,7 @@ import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.SwipeEvent;
import org.zkoss.zul.Anchorlayout; import org.zkoss.zul.Anchorlayout;
import org.zkoss.zul.Vlayout; import org.zkoss.zul.Vlayout;
@ -127,6 +128,17 @@ public class WindowContainer extends AbstractUIPart
setTabTitle(title, tab); setTabTitle(title, tab);
} }
tab.setClosable(closeable); tab.setClosable(closeable);
tab.addEventListener(Events.ON_SWIPE, new EventListener<SwipeEvent>() {
@Override
public void onEvent(SwipeEvent event) throws Exception {
Tab tab = (Tab) event.getTarget();
if (tab.isClosable()
&& ("right".equals(event.getSwipeDirection()) || "left".equals(event.getSwipeDirection()))) {
tab.onClose();
}
}
});
// fix scroll position lost coming back into a grid view tab // fix scroll position lost coming back into a grid view tab
tab.addEventListener(Events.ON_SELECT, new EventListener<Event>() { tab.addEventListener(Events.ON_SELECT, new EventListener<Event>() {

View File

@ -1,8 +1,8 @@
<div class="tabs arrows" xmlns:w="client" forward="onMoveDate(${arg.type})" <div class="tabs arrows" xmlns:w="client" forward="onMoveDate(${arg.type})"
w:unbind_="function(){this.$unbind_();jq(this).unbind('mouseover').unbind('mouseout');}"> w:unbind_="function(skipper, after){this.$unbind_(skipper, after);jq(this).unbind('mouseover').unbind('mouseout');}">
<attribute w:name="bind_"><![CDATA[ <attribute w:name="bind_"><![CDATA[
function () { function (desktop, skipper, after) {
this.$bind_(); this.$bind_(desktop, skipper, after);
jq(this).bind('mouseover',function(evt){jq(evt.currentTarget).addClass('arrow-over');}) jq(this).bind('mouseover',function(evt){jq(evt.currentTarget).addClass('arrow-over');})
.bind('mouseout',function(evt){jq(evt.currentTarget).removeClass('arrow-over');}); .bind('mouseout',function(evt){jq(evt.currentTarget).removeClass('arrow-over');});
} }