/* * MainSplitPanel.java * * Copyright (C) 2009-12 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.studio.client.workbench.ui; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.SplitterResizedEvent; import com.google.gwt.user.client.ui.SplitterResizedHandler; import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import org.rstudio.core.client.js.JsObject; import org.rstudio.studio.client.application.events.EventBus; import org.rstudio.studio.client.common.NotifyingSplitLayoutPanel; import org.rstudio.studio.client.workbench.model.ClientState; import org.rstudio.studio.client.workbench.model.Session; import org.rstudio.studio.client.workbench.model.helper.JSObjectStateValue; public class MainSplitPanel extends NotifyingSplitLayoutPanel implements SplitterResizedHandler { private static class State extends JavaScriptObject { protected State() {} public native final boolean hasSplitterPos() /*-{ return typeof(this.splitterpos) != 'undefined'; }-*/; public native final int getSplitterPos() /*-{ return this.splitterpos; }-*/; public native final void setSplitterPos(int pos) /*-{ this.splitterpos = pos; }-*/; public native final boolean hasPanelWidth() /*-{ return typeof(this.panelwidth) != 'undefined'; }-*/; public native final int getPanelWidth() /*-{ return this.panelwidth; }-*/; public native final void setPanelWidth(int width) /*-{ this.panelwidth = width; }-*/; public native final boolean hasWindowWidth() /*-{ return typeof(this.windowwidth) != 'undefined'; }-*/; public native final int getWindowWidth() /*-{ return this.windowwidth; }-*/; public native final void setWindowWidth(int width) /*-{ this.windowwidth = width; }-*/; public static boolean equals(State a, State b) { if (a == null ^ b == null) return false; if (a == null) return true; if (a.hasSplitterPos() ^ b.hasSplitterPos()) return false; if (a.hasSplitterPos() && a.getSplitterPos() != b.getSplitterPos()) return false; if (a.hasPanelWidth() ^ b.hasPanelWidth()) return false; if (a.hasPanelWidth() && a.getPanelWidth() != b.getPanelWidth()) return false; if (a.hasWindowWidth() ^ b.hasWindowWidth()) return false; if (a.hasWindowWidth() && a.getWindowWidth() != b.getWindowWidth()) return false; return true; } } @Inject public MainSplitPanel(EventBus events, Session session) { super(3, events); session_ = session; addSplitterResizedHandler(this); } public void initialize(Widget left, Widget right) { left_ = left; right_ = right; new JSObjectStateValue(GROUP_WORKBENCH, KEY_RIGHTPANESIZE, ClientState.PERSISTENT, session_.getSessionInfo().getClientState(), false) { @Override protected void onInit(JsObject value) { State state = value == null ? null : (State)value.cast(); if (state != null && state.hasSplitterPos()) { if (state.hasPanelWidth() && state.hasWindowWidth() && state.getWindowWidth() != Window.getClientWidth()) { int delta = state.getWindowWidth() - state.getPanelWidth(); int offsetWidth = Window.getClientWidth() - delta; double pct = (double)state.getSplitterPos() / state.getPanelWidth(); addEast(right_, pct * offsetWidth); } else { addEast(right_, state.getSplitterPos()); } } else { addEast(right_, Window.getClientWidth() * 0.45); } Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { enforceBoundaries(); } }); } @Override protected JsObject getValue() { State state = JavaScriptObject.createObject().cast(); state.setPanelWidth(getOffsetWidth()); state.setWindowWidth(Window.getClientWidth()); state.setSplitterPos(right_.getOffsetWidth()); return state.cast(); } @Override protected boolean hasChanged() { JsObject newValue = getValue(); if (!State.equals(lastKnownValue_, (State) newValue.cast())) { lastKnownValue_ = newValue.cast(); return true; } return false; } private State lastKnownValue_; }; add(left); setWidgetMinSize(right_, 0); } @Override protected void onLoad() { super.onLoad(); deferredSaveWidthPercent(); } public void onSplitterResized(SplitterResizedEvent event) { enforceBoundaries(); deferredSaveWidthPercent(); } private void enforceBoundaries() { LayoutData layoutData = (LayoutData) right_.getLayoutData(); if (layoutData != null && getOffsetWidth() != 0 && layoutData.size > getOffsetWidth() - 3) { layoutData.size = getOffsetWidth() - 3; forceLayout(); } } private void deferredSaveWidthPercent() { Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { splitPercent_ = null; int panelWidth = getOffsetWidth(); assert panelWidth > 0; assert isVisible() && isAttached(); if (panelWidth > 0) splitPercent_ = (double)right_.getOffsetWidth() / panelWidth; previousOffsetWidth_ = panelWidth; } }); } @Override public void onResize() { super.onResize(); int offsetWidth = getOffsetWidth(); if ((previousOffsetWidth_ == null || offsetWidth != previousOffsetWidth_.intValue()) && splitPercent_ != null) { LayoutData layoutData = (LayoutData) right_.getLayoutData(); if (layoutData == null) return; layoutData.size = splitPercent_ * offsetWidth; previousOffsetWidth_ = offsetWidth; // Defer actually updating the layout, so that if we receive many // mouse events before layout/paint occurs, we'll only update once. if (layoutCommand_ == null) { layoutCommand_ = new Command() { public void execute() { layoutCommand_ = null; forceLayout(); } }; Scheduler.get().scheduleDeferred(layoutCommand_); } } } private Double splitPercent_ = null; private Integer previousOffsetWidth_ = null; private final Session session_; @SuppressWarnings("unused") private Widget left_; private Widget right_; private static final String GROUP_WORKBENCH = "workbenchp"; private static final String KEY_RIGHTPANESIZE = "rightpanesize"; private Command layoutCommand_; }