// ========================================================================== // Project: SproutCore - JavaScript Application Framework // Copyright: ©2006-2010 Sprout Systems, Inc. and contributors. // Portions ©2008-2011 Apple Inc. All rights reserved. // License: Licensed under MIT license (see license.js) // ========================================================================== /** Tells the SplitThumb to automatically choose which child of the SplitView to move in response to touch or mouse events in an SC.SplitThumb. */ SC.MOVES_AUTOMATIC_CHILD = 'moves-automatic-child'; /** Tells the SplitThumb to move the child of the SplitView that is either the SplitThumb or a parent of it. */ SC.MOVES_CHILD = 'moves-child'; /** Tells the SplitThumb to move the child of the SplitView that preceeds the child that is either the SplitThumb or a parent of it. */ SC.MOVES_PREVIOUS_CHILD = 'moves-previous-child'; /** Tells the SplitThumb to move the child of the SplitVie that comes after the child that is either the SplitThumb or a parent of it. */ SC.MOVES_NEXT_CHILD = 'moves-next-child'; /** @namespace A SplitThumb may be placed inside any view in a SplitView, and can even be a direct child of the SplitView. It forwards its events to the SplitView to control the movement of a divider or another child of the SplitView. Using a view that mixes in SplitThumb, you can place a handle that moves the divider anywhere inside the SplitView's view tree. SplitThumb will automatically choose which divider to move. It's choice will almost always be correct for 2-pane layouts. However, in 3-pane layouts, you may want to adjust the way it chooses. You can adjust the behavior by setting the movesSibling property to SC.MOVES_NEXT_CHILD or SC.MOVES_PREVIOUS_CHILD. If your ThumbView is inside the middle pane, for instance, this would tell it whether the ThumbView should move the divider to the left, or the divider to the right. */ SC.SplitThumb = { /** * The child which should be moved by any mouse or touch events. Should usually * be a divider view. * * The default implementation calculates the child by first finding * the ancestor of this view that is a direct child of the SplitView, * and then either returning it or one of its immediate siblings, depending * on the value of the movesSibling property. * * @property SC.View */ movesChild: function() { var view = this, child, splitView = this.get('splitView'), sibling = this.get('movesSibling'); while (view && view !== splitView) { child = view; view = view.get('parentView'); } if (sibling === SC.MOVES_NEXT_CHILD) return child.nextView; if (sibling === SC.MOVES_PREVIOUS_CHILD) return child.previousView; if (sibling === SC.MOVES_AUTOMATIC_CHILD) { if (!child.nextView) return child.previousView; if (!child.previousView) return child.nextView; } return child; }.property('splitView', 'movesSibling').cacheable(), /** * Decides whether an ancestor of this SplitThumb view or one of its * siblings is to be moved in response to events. * * Usually, you want events to move a divider. If the SplitThumb is inside * a view which is _not_ a divider, you probably want to move one of the * view's siblings-one that _is_ a divider. * * You can tell SC.SplitThumb to: * * - SC.MOVES_AUTOMATIC_CHILD: if the SplitView child is either first or last, * moves the adjacent child (likely a divider). Otherwise moves the child itself. * * - SC.MOVES_CHILD: moves the child itself, not one of its siblings. Divider * views could use this setting. * * - SC.MOVES_PREVIOUS_CHILD: moves the previous child. For instance, in a * two-pane setup, if the SplitThumb is in the rightmost child, this will * move the divider between the two children. * * - SC.MOVES_NEXT_CHILD: moves the next child. * * @type TYPE */ movesSibling: SC.MOVES_AUTOMATIC_CHILD, /** * The SplitView that contains the child views to be adjusted. * * This is computed to be the first SplitView found in a search * up the view hierarchy. You can substitute your own SplitView * * @property SC.SplitView */ splitView: function() { var view = this ; while (view && !view.isSplitView) view = view.get('parentView') ; return view ; }.property('parentView').cacheable(), /** * The layoutDirection of the SplitView. This is observed so that we * can update positioning if the layoutDirection changes but the position * and size properties do not. * * NOTE: duplicated in SplitChild because both this and SplitChild use it. * * @type {LayoutDirection} */ // NOTE: While an edge case, this is implemented because it makes it _much_ // easier to write the sample in the Test Controls app. splitViewLayoutDirection: null, splitViewLayoutDirectionBinding: SC.Binding.oneWay('*splitView.layoutDirection'), /** * The name of the CSS cursor that should be used for splitting. * The containing SplitView will adopt this cursor if and when this * view is dragged. * * Computed based on the SplitView's layoutDirection. * * @type {String} */ splitCursorStyle: function() { if (this.get('splitViewLayoutDirection') === SC.LAYOUT_HORIZONTAL) { return 'ew-resize'; } else { return 'ns-resize'; } }.property('splitViewLayoutDirection').cacheable(), splitCursorStyleDidChange: function() { if (this._isDragging) { this.get('splitView').set('splitChildCursorStyle', this.get('splitCursorStyle')); } this.$().css('cursor', this.get('splitCursorStyle')); }.observes('splitCursorStyle'), /** * @private * Renders the cursor for the view as defined by this view's splitCursor * property. */ renderMixin: function(context) { context.css('cursor', this.get('splitCursorStyle')); }, // // EVENT HANDLING // touchStart: function(touch) { this._isDragging = YES; var splitView = this.get('splitView'); splitView.beginLiveResize(); this._scst_mouseStartPosition = splitView.get('layoutDirection') === SC.LAYOUT_HORIZONTAL ? touch.pageX : touch.pageY; this._scst_childStartPosition = splitView.getPositionForChild(this.get('movesChild')); return YES; }, touchesDragged: function(evt) { var splitView = this.get('splitView'); var mousePosition = splitView.get('layoutDirection') === SC.LAYOUT_HORIZONTAL ? evt.pageX : evt.pageY; var diff = mousePosition - this._scst_mouseStartPosition, start = this._scst_childStartPosition; splitView.adjustPositionForChild(this.get('movesChild'), start + diff); return YES; }, touchEnd: function(touch) { this._isDragging = NO; var splitView = this.get('splitView'); var mousePosition = splitView.get('layoutDirection') === SC.LAYOUT_HORIZONTAL ? touch.pageX : touch.pageY; var diff = mousePosition - this._scst_mouseStartPosition, start = this._scst_childStartPosition; splitView.adjustPositionForChild(this.get('movesChild'), start + diff); splitView.set('splitChildCursorStyle', null); splitView.endLiveResize(); return YES; }, mouseDown: function(evt) { var splitView = this.get('splitView'); splitView.set('splitChildCursorStyle', this.get('splitCursorStyle')); return this.touchStart(evt); }, mouseDragged: function(evt) { return this.touchesDragged(evt); }, mouseUp: function(evt) { this.get('splitView').set('splitChildCursorStyle', null); return this.touchEnd(evt); } };