    /**
     * Component layout for grid column headers which have a title element at the top followed by content.
     * @private
     */
    Ext.define('Ext.grid.ColumnComponentLayout', {
        extend: 'Ext.layout.component.Auto',
        alias: 'layout.columncomponent',

        type: 'columncomponent',

        setWidthInDom: true,

        beginLayout: function(ownerContext) {
            var me = this;

            me.callParent(arguments);
            ownerContext.titleContext = ownerContext.getEl('titleEl');
            ownerContext.triggerContext = ownerContext.getEl('triggerEl');
        },

        beginLayoutCycle: function(ownerContext) {
            var me = this,
                owner = me.owner;

            me.callParent(arguments);

            // If shrinkwrapping, allow content width to stretch the element
            if (ownerContext.widthModel.shrinkWrap) {
                owner.el.setWidth('');
            }

            // When we are the last subheader, bordering is provided by our owning header, so we need
            // to set border width to zero
            var borderRightWidth = owner.isLast && owner.isSubHeader ? '0' : '';
            if (borderRightWidth !== me.lastBorderRightWidth) {
                owner.el.dom.style.borderRightWidth = me.lasBorderRightWidth = borderRightWidth;
            }

            owner.titleEl.setStyle({
                paddingTop: '',  // reset back to default padding of the style
                paddingBottom: ''
            });
        },

        // If not shrink wrapping, push height info down into child items
        publishInnerHeight: function(ownerContext, outerHeight) {
            // TreePanels (and grids with hideHeaders: true) set their column container height to zero ti hide them.
            // This is because they need to lay out in order to calculate widths for the columns (eg flexes).
            // If there is no height to lay out, bail out early.
            if (!outerHeight) {
                return;
            }

            var me = this,
                owner = me.owner,
                innerHeight = outerHeight - ownerContext.getBorderInfo().height,
                availableHeight = innerHeight,
                textHeight,
                titleHeight,
                pt, pb;

            // We do not have enough information to get the height of the titleEl
            if (!owner.noWrap && !ownerContext.hasDomProp('width')) {
                me.done = false;
                return;
            }

            // If we are not a container, but just have HTML content...
            if (ownerContext.hasRawContent) {
                titleHeight = availableHeight;

                // Vertically center the header text and ensure titleContext occupies availableHeight
                textHeight = owner.textEl.getHeight();
                if (textHeight) {
                    availableHeight -= textHeight;
                    if (availableHeight > 0) {
                        pt = Math.floor(availableHeight / 2);
                        pb = availableHeight - pt;
                        ownerContext.titleContext.setProp('padding-top', pt);
                        ownerContext.titleContext.setProp('padding-bottom', pb);
                    }
                }
            }

            // There are child items
            else {
                titleHeight = owner.titleEl.getHeight();
                ownerContext.setProp('innerHeight', innerHeight - titleHeight, false);
            }
            // Only IE6 and IEQuirks needs this.
            // This is why we maintain titleHeight when setting it.
            if ((Ext.isIE6 || Ext.isIEQuirks) && ownerContext.triggerContext) {
                ownerContext.triggerContext.setHeight(titleHeight);
            }

        },

        // We do not need the Direct2D sub pixel measurement here. Just the offsetHeight will do.
        // TODO: When https://sencha.jira.com/browse/EXTJSIV-7734 is fixed to not do subpixel adjustment on height,
        // remove this override.
        measureContentHeight: function(ownerContext) {
            return ownerContext.el.dom.offsetHeight;
        },

        publishOwnerHeight: function(ownerContext, contentHeight) {
            this.callParent(arguments);
            // Only IE6 and IEQuirks needs this.
            // This is why we maintain titleHeight when setting it.
            if ((Ext.isIE6 || Ext.isIEQuirks) && ownerContext.triggerContext) {
                ownerContext.triggerContext.setHeight(contentHeight);
            }
        },

        // If not shrink wrapping, push width info down into child items
        publishInnerWidth: function(ownerContext, outerWidth) {
            // If we are acting as a container, publish the innerWidth for the ColumnLayout to use
            if (!ownerContext.hasRawContent) {
                ownerContext.setProp('innerWidth', outerWidth - ownerContext.getBorderInfo().width, false);
            }
        },

        // Push content height outwards when we are shrinkwrapping
        calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
            var result = this.callParent(arguments);

            // If we are NOT a group header, we just use the auto component's measurement
            if (!ownerContext.hasRawContent) {
                if (this.owner.noWrap || ownerContext.hasDomProp('width')) {
                    return contentHeight + this.owner.titleEl.getHeight() + ownerContext.getBorderInfo().height;
                }

                // We do not have the information to return the height yet because we cannot know
                // the final height of the text el
                return null;
            }
            return result;
        },

        // Push content width outwards when we are shrinkwrapping
        calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) {
            var owner = this.owner,
                inner = Math.max(contentWidth, owner.textEl.getWidth() + ownerContext.titleContext.getPaddingInfo().width),
                padWidth = ownerContext.getPaddingInfo().width,
                triggerOffset = this.getTriggerOffset(owner, ownerContext);

            return inner + padWidth + triggerOffset;
        },

        getTriggerOffset: function(owner, ownerContext) {
            var width = 0;
            if (ownerContext.widthModel.shrinkWrap && !owner.menuDisabled) {
                // If we have any children underneath, then we already have space reserved
                if (owner.query('>:not([hidden])').length === 0) {
                    width = owner.self.triggerElWidth;
                }
            }
            return width;
        }
    });