diff --git a/apps/child_view_layout_demo/resources/demo_page.js b/apps/child_view_layout_demo/resources/demo_page.js index e318cfa..d1bf301 100644 --- a/apps/child_view_layout_demo/resources/demo_page.js +++ b/apps/child_view_layout_demo/resources/demo_page.js @@ -4,6 +4,8 @@ // ========================================================================== /*globals ChildViewLayoutDemo */ +sc_require('views/form_view'); + // This page describes the main user interface for your application. ChildViewLayoutDemo.demoPage = SC.Page.design({ @@ -11,159 +13,25 @@ ChildViewLayoutDemo.demoPage = SC.Page.design({ // The main pane is made visible on screen as soon as your app is loaded. // Add childViews to this pane for views to display immediately on page // load. - demoContent: SC.View.design({ - childViews: ['formView'], + demoContent: SC.TabView.extend({ - formView: SC.View.extend({ + layout: { border: 20 }, - // ----------------------------------------------------------------------- - // Properties - // + itemTitleKey: 'title', + itemValueKey: 'value', - childViews: ['v1', 'v2', 'v3', 'v4', 'label'], - classNames: ['wrapper-view'], - layout: { border: 2, centerX: 0, centerY: 0, width: 300 }, // No height required. + items: [ + { title: 'Vertical', value: 'verticalFormView' }, + { title: 'Horizontal', value: 'horizontalFormView' } + ], - // Layout the child views vertically. + nowShowing: 'verticalFormView', + + childViews: ['verticalFormView'], + + verticalFormView: ChildViewLayoutDemo.FormView.extend({ + layout: { border: 2, centerX: 0, centerY: 0, width: 300 }, // No height required. childViewLayout: SC.View.VERTICAL_STACK, - childViewLayoutOptionsBinding: SC.Binding.oneWay('ChildViewLayoutDemo.childViewLayoutOptions'), - - // Smoothly resize. - transitionAdjust: SC.View.SMOOTH_ADJUST, - transitionAdjustOptions: { duration: 0.2 }, - - /** @private SC.View. Don't transition when doing live resize. */ - beginLiveResize: function () { - sc_super(); - - this.set('transitionAdjust', null); - }, - - /** @private SC.View. Allowing transitioning again. */ - endLiveResize: function () { - sc_super(); - - this.set('transitionAdjust', SC.View.SMOOTH_ADJUST); - }, - - // ----------------------------------------------------------------------- - // SC.DropTarget Support - // - - /** @private SC.DropTarget protocol. */ - isDropTarget: true, - - /** @private SC.DropTarget protocol. */ - computeDragOperations: function (drag, evt, op) { - return SC.DRAG_MOVE; - }, - - /** @private SC.DropTarget protocol. */ - dragUpdated: function (drag, evt) { - // console.log('%@ - dragUpdated(%@, evt): dragView: %@'.fmt(this, drag, drag.dragView)); - var point = { x: evt.clientX, y: evt.clientY }, - childViews = this.get('childViews'), - dragView = drag.dragView, - options = this.get('childViewLayoutOptions'); - - // Cycle through the child views and find whichever one it is over and insert a drop placeholder child at that point. - for (var i = 0, len = childViews.get('length'); i < len; i++) { - var childView = childViews.objectAt(i), - frame = SC.clone(childView.get('frame')), - layout = childView.get('layout'), - borderingChildView, pageFrame; - - // Adjust the frame all the way to the left & right. - frame.x -= layout.left; - frame.width += layout.left + layout.right; - - // If followed by the drop zone, cut our frame to the top two thirds. - borderingChildView = childViews.objectAt(i + 1); - if (borderingChildView && borderingChildView === dragView) { - frame.height = frame.height / 1.5; - } - - // If preceded by the drop zone, cut our frame to the bottom two thirds. - borderingChildView = childViews.objectAt(i - 1); - if (borderingChildView && borderingChildView === dragView) { - frame.y += frame.height / 3; - frame.height = frame.height / 1.5; - } - - // Stretch the first frame up and the last frame down, which allows you - // to drag in from the top or bottom edge and get the top or bottom - // drop zone. - if (i === 0) { - frame.y -= options.paddingBefore; - frame.height += options.paddingBefore; - } - if (i === len - 1) { - frame.height += options.paddingAfter; - } - - // Convert the frame into window coordinates and check the point. - pageFrame = this.convertFrameToView(frame, null); - if (SC.pointInRect(point, pageFrame)) { - break; - } - } - - if (i < len) { - // Move the drag view to the target child view index. - var curDropViewIdx = childViews.indexOf(dragView); - if (curDropViewIdx !== i) { - // Remove the view from the old spot. - if (curDropViewIdx >= 0) { - this.removeChild(dragView); - } - - // Insert the view in the new spot. - this.insertBefore(dragView, childViews.objectAt(i)); - } - } - }, - - /** @private SC.DropTarget protocol. */ - dragStarted: function (drag, evt) { - var dragView = drag.dragView; - - dragView.set('transitionAdjust', null); - }, - - /** @private SC.DropTarget protocol. */ - dragEntered: function (drag, evt) { - // var dragView = drag.dragView; - }, - - /** @private SC.DropTarget protocol. */ - dragExited: function (drag, evt) { - var dragView = drag.dragView; - - if (this.get('childViews').indexOf(dragView) >= 0) { - this.removeChild(dragView); - } - }, - - /** @private SC.DropTarget protocol. */ - dragEnded: function (drag, evt) { - var dragView = drag.dragView; - - dragView.set('transitionAdjust', SC.View.SMOOTH_ADJUST); - }, - - /** @private SC.DropTarget protocol. */ - acceptDragOperation: function (drag, op) { - return YES; - }, - - /** @private SC.DropTarget protocol. */ - performDragOperation: function (drag, op) { - return SC.DRAG_NONE; - }, - - // ----------------------------------------------------------------------- - // Child Views - // v1: ChildViewLayoutDemo.SampleView.design({ classNames: ['view1'], @@ -191,11 +59,46 @@ ChildViewLayoutDemo.demoPage = SC.Page.design({ label: SC.LabelView.extend({ classNames: ['form-label'], - layout: { height: 30, left: 10, right: 10, bottom: 0 }, + layout: { height: 30, left: 10, right: 10, bottom: 0 }, localize: true, useAbsoluteLayout: true, value: "_DragLabel" }) + }), + + horizontalFormView: ChildViewLayoutDemo.FormView.extend({ + layout: { border: 2, centerX: 0, centerY: 0, height: 300 }, // No width required. + childViewLayout: SC.View.HORIZONTAL_STACK, + + v1: ChildViewLayoutDemo.SampleView.design({ + classNames: ['view1'], + layout: { border: 2, top: 5, bottom: 30, width: 100 }, // No left required. + value: 'A' + }), + + v2: ChildViewLayoutDemo.SampleView.design({ + classNames: ['view2'], + layout: { border: 2, top: 5, bottom: 30, width: 150 }, // No left required. + value: 'B' + }), + + v3: ChildViewLayoutDemo.SampleView.design({ + classNames: ['view3'], + layout: { border: 2, top: 5, bottom: 30, width: 75 }, // No left required. + value: 'C' + }), + + v4: ChildViewLayoutDemo.SampleView.design({ + classNames: ['view4'], + layout: { border: 2, top: 5, bottom: 30, width: 125 }, // No left required. + value: 'D' + }), + + label: SC.View.extend({ + layout: { width: 0 }, + useAbsoluteLayout: true + }) + }) // UNUSED. diff --git a/apps/child_view_layout_demo/views/form_view.js b/apps/child_view_layout_demo/views/form_view.js new file mode 100644 index 0000000..4a4c225 --- /dev/null +++ b/apps/child_view_layout_demo/views/form_view.js @@ -0,0 +1,149 @@ +sc_require('views/sample_view'); + +ChildViewLayoutDemo.FormView = SC.View.extend({ + + // ----------------------------------------------------------------------- + // Properties + // + + childViews: ['v1', 'v2', 'v3', 'v4', 'label'], + classNames: ['wrapper-view'], + + // Layout the child views vertically by default + childViewLayout: SC.View.VERTICAL_STACK, + childViewLayoutOptionsBinding: SC.Binding.oneWay('ChildViewLayoutDemo.childViewLayoutOptions'), + + // Smoothly resize. + transitionAdjust: SC.View.SMOOTH_ADJUST, + transitionAdjustOptions: { duration: 0.2 }, + + /** @private SC.View. Don't transition when doing live resize. */ + beginLiveResize: function () { + sc_super(); + + this.set('transitionAdjust', null); + }, + + /** @private SC.View. Allowing transitioning again. */ + endLiveResize: function () { + sc_super(); + + this.set('transitionAdjust', SC.View.SMOOTH_ADJUST); + }, + + // ----------------------------------------------------------------------- + // SC.DropTarget Support + // + + /** @private SC.DropTarget protocol. */ + isDropTarget: true, + + /** @private SC.DropTarget protocol. */ + computeDragOperations: function (drag, evt, op) { + return SC.DRAG_MOVE; + }, + + /** @private SC.DropTarget protocol. */ + dragUpdated: function (drag, evt) { + // console.log('%@ - dragUpdated(%@, evt): dragView: %@'.fmt(this, drag, drag.dragView)); + var point = { x: evt.clientX, y: evt.clientY }, + childViews = this.get('childViews'), + dragView = drag.dragView, + options = this.get('childViewLayoutOptions'); + + // Cycle through the child views and find whichever one it is over and insert a drop placeholder child at that point. + for (var i = 0, len = childViews.get('length'); i < len; i++) { + var childView = childViews.objectAt(i), + frame = SC.clone(childView.get('frame')), + layout = childView.get('layout'), + borderingChildView, pageFrame; + + // Adjust the frame all the way to the left & right. + frame.x -= layout.left; + frame.width += layout.left + layout.right; + + // If followed by the drop zone, cut our frame to the top two thirds. + borderingChildView = childViews.objectAt(i + 1); + if (borderingChildView && borderingChildView === dragView) { + frame.height = frame.height / 1.5; + } + + // If preceded by the drop zone, cut our frame to the bottom two thirds. + borderingChildView = childViews.objectAt(i - 1); + if (borderingChildView && borderingChildView === dragView) { + frame.y += frame.height / 3; + frame.height = frame.height / 1.5; + } + + // Stretch the first frame up and the last frame down, which allows you + // to drag in from the top or bottom edge and get the top or bottom + // drop zone. + if (i === 0) { + frame.y -= options.paddingBefore; + frame.height += options.paddingBefore; + } + if (i === len - 1) { + frame.height += options.paddingAfter; + } + + // Convert the frame into window coordinates and check the point. + pageFrame = this.convertFrameToView(frame, null); + if (SC.pointInRect(point, pageFrame)) { + break; + } + } + + if (i < len) { + // Move the drag view to the target child view index. + var curDropViewIdx = childViews.indexOf(dragView); + if (curDropViewIdx !== i) { + // Remove the view from the old spot. + if (curDropViewIdx >= 0) { + this.removeChild(dragView); + } + + // Insert the view in the new spot. + this.insertBefore(dragView, childViews.objectAt(i)); + } + } + }, + + /** @private SC.DropTarget protocol. */ + dragStarted: function (drag, evt) { + var dragView = drag.dragView; + + dragView.set('transitionAdjust', null); + }, + + /** @private SC.DropTarget protocol. */ + dragEntered: function (drag, evt) { + // var dragView = drag.dragView; + }, + + /** @private SC.DropTarget protocol. */ + dragExited: function (drag, evt) { + var dragView = drag.dragView; + + if (this.get('childViews').indexOf(dragView) >= 0) { + this.removeChild(dragView); + } + }, + + /** @private SC.DropTarget protocol. */ + dragEnded: function (drag, evt) { + var dragView = drag.dragView; + + dragView.set('transitionAdjust', SC.View.SMOOTH_ADJUST); + }, + + /** @private SC.DropTarget protocol. */ + acceptDragOperation: function (drag, op) { + return YES; + }, + + /** @private SC.DropTarget protocol. */ + performDragOperation: function (drag, op) { + return SC.DRAG_NONE; + } + +}); diff --git a/apps/child_view_layout_demo/views/sample_view.js b/apps/child_view_layout_demo/views/sample_view.js index 4906b5d..5f0d5cd 100644 --- a/apps/child_view_layout_demo/views/sample_view.js +++ b/apps/child_view_layout_demo/views/sample_view.js @@ -34,7 +34,18 @@ ChildViewLayoutDemo.SampleView = SC.View.extend({ resizeHandle: SC.View.extend({ classNames: ['resize-handle'], - layout: { bottom: 0, height: 11 }, + + parentChildViewLayoutBinding: 'ChildViewLayoutDemo*demoPage.demoContent.containerView.childViews.firstObject.childViewLayout', + + layout: function() { + var layout = this.get('parentChildViewLayout'); + + if (layout == SC.View.HORIZONTAL_STACK) { + return { right: 0, width: 11 }; + } + + return { bottom: 0, height: 11 }; + }.property('parentChildViewLayout'), render: function (context) { context.begin().addClass('handle-image').end(); @@ -44,28 +55,47 @@ ChildViewLayoutDemo.SampleView = SC.View.extend({ // Cache the initial vertical offset and parent height. var parentView = this.get('parentView'), wrapperView = parentView.get('parentView'), - frame = parentView.get('borderFrame'); + frame = parentView.get('borderFrame'), + layout = this.get('parentChildViewLayout'); // Indicate that we are resizing. wrapperView.beginLiveResize(); - this._initialY = evt.clientY; - this._initialHeight = frame.height; + if (layout == SC.View.HORIZONTAL_STACK) { + this._initialX = evt.clientX; + this._initialWidth = frame.width; + } else { + this._initialY = evt.clientY; + this._initialHeight = frame.height; + } return true; }, mouseDragged: function (evt) { var parentView = this.get('parentView'), + layout = this.get('parentChildViewLayout'), height, + width, offset; - - offset = evt.clientY - this._initialY; - // Parent view is centered, so we double the offset to keep the dragger under the mouse (for aesthetic purposes - the view would - // continue to get the events even if the mouse is no longer over it, as RootResponder routes subsequent mouse events to the view - // which handled mouseDown). - height = Math.max(75, this._initialHeight + (offset * 2)); - parentView.adjust('height', height); + + if (layout == SC.View.HORIZONTAL_STACK) { + offset = evt.clientX - this._initialX; + + // Parent view is centered, so we double the offset to keep the dragger under the mouse (for aesthetic purposes - the view would + // continue to get the events even if the mouse is no longer over it, as RootResponder routes subsequent mouse events to the view + // which handled mouseDown). + width = Math.max(75, this._initialWidth + (offset * 2)); + parentView.adjust('width', width); + } else { + offset = evt.clientY - this._initialY; + + // Parent view is centered, so we double the offset to keep the dragger under the mouse (for aesthetic purposes - the view would + // continue to get the events even if the mouse is no longer over it, as RootResponder routes subsequent mouse events to the view + // which handled mouseDown). + height = Math.max(75, this._initialHeight + (offset * 2)); + parentView.adjust('height', height); + } }, mouseUp: function (evt) { @@ -76,7 +106,11 @@ ChildViewLayoutDemo.SampleView = SC.View.extend({ wrapperView.endLiveResize(); // Clean up. - delete this._initialPoint; + delete this._initialX; + delete this._initialY; + delete this._initialWidth; + delete this._initialHeight; + parentView.set('transitionAdjust', SC.View.SMOOTH_ADJUST); return true;