ExtJS 4 中的自定义溢出处理程序实现

Posted

技术标签:

【中文标题】ExtJS 4 中的自定义溢出处理程序实现【英文标题】:Custom overflowHandler implementation in ExtJS 4 【发布时间】:2012-09-29 14:47:26 【问题描述】:

我有两个toolbars 的面板。如何实现自定义类以用作overflowHandler,它将组件移动到第一个工具栏溢出时的第二个工具栏?

我尝试使用Ext.layout.container.boxOverflow.Menu 的代码,但我的第二个工具栏只是隐藏了。

这是我的代码,它与来自 ExtJS 4 分发的 toolbar overflow 示例混合在一起。

Ext.require(['Ext.window.Window', 'Ext.toolbar.Toolbar', 'Ext.menu.ColorPicker', 'Ext.form.field.Date']);
Ext.onReady(function()

    /**
     * Override for implementing tbar2 
     */
    Ext.override(Ext.panel.Panel, 
        bridgeToolbars : function () 
            var toolbar;
            this.callParent(arguments);
            if (this.tbar2) 
                if (Ext.isArray(this.tbar2)) 
                    toolbar = 
                        xtype : 'toolbar',
                        items : this.tbar2
                    ;
                
                else if (!toolbar.xtype) 
                    toolbar.xtype = 'toolbar';
                
                toolbar.dock = 'top';
                toolbar.isTbar2 = true;
                this.dockedItems = this.dockedItems.concat(toolbar);
                this.tbar2 = null;
            
        ,
        onRender       : function () 
            this.callParent(arguments);
            var topBars = this.getDockedItems('toolbar[dock="top"]'),
                i,
                len;
            for (i = 0, len = topBars.length; i < len; i++) 
                if (topBars[i].isTbar2) 
                    this.tbar2 = topBars[i];
                    break;
                
            
        ,
        /**
         * Lazy creates new toolbar and returns it
         * @param Ext.panel.Panel panel
         * @param String position
         * @return Ext.toolbar.Toolbar
         */
        getDynamicTBar : function (position) 
            var panel = this,
                params,
                tb;
            position = position || 'top';
            if (position === 'tbar2') 
                tb = panel.tbar2;
                params = dock : 'top', isTbar2 : true;
            
            else 
                tb = panel.getDockedItems('toolbar[dock="' + position + '"]');
                params = dock : position;
                if (tb.length > 0) 
                    tb = tb[0];
                
            
            if (!tb) 
                console.log('created tb at ' + position);
                tb = Ext.create('Ext.toolbar.Toolbar', params);
                panel.addDocked(tb);
            
            return tb;
        
    );

    Ext.define('Ext.layout.container.boxOverflow.TBar2', 
        extend : 'Ext.layout.container.boxOverflow.None',

        constructor : function () 
            this.tbar2Items = [];
            return this.callParent(arguments);
        ,

        beginLayout : function (ownerContext) 
            this.callParent(arguments);
            this.clearOverflow(ownerContext);
        ,

        beginLayoutCycle : function (ownerContext, firstCycle) 
            this.callParent(arguments);
            if (!firstCycle) 
                this.clearOverflow(ownerContext);
                this.layout.cacheChildItems(ownerContext);
            
        ,

        getOverflowCls : function () 
            return Ext.baseCSSPrefix + this.layout.direction + '-box-overflow-body';
        ,

        _asLayoutRoot :  isRoot : true ,

        clearOverflow : function () 
            if (this.tbar2) 
                this.tbar2.suspendLayouts();
                this.tbar2.hide();
                this.tbar2.resumeLayouts(this._asLayoutRoot);
            
            this.tbar2Items.length = 0;
        ,

        handleOverflow : function (ownerContext) 

            var me = this,
                layout = me.layout,
                owner = layout.owner,
                names = layout.getNames(),
                startProp = names.x,
                sizeProp = names.width,
                plan = ownerContext.state.boxPlan,
                available = plan.targetSize[sizeProp],
                childItems = ownerContext.childItems,
                len = childItems.length,
                childContext,
                comp, i, props,
                tbarOwner = owner.ownerCt;
            owner.suspendLayouts();
            // Hide all items which are off the end, and store them to allow them to be restored
            // before each layout operation.
            me.tbar2Items.length = 0;
            for (i = 0; i < len; i++) 
                childContext = childItems[i];
                props = childContext.props;
                if (props[startProp] + props[sizeProp] > available) 
                    comp = childContext.target;
                    me.tbar2Items.push(comp);
                    owner.remove(comp, false);
                
            
            owner.resumeLayouts();
            if (!me.tbar2 && (tbarOwner instanceof Ext.panel.Panel)) 
                me.tbar2 = tbarOwner.getDynamicTBar('tbar2');
            
            me.tbar2.suspendLayouts();
            me.tbar2.show();

            Ext.each(me.tbar2Items, function(item, index) 
                me.tbar2.add(item);
            );
            me.tbar2.resumeLayouts(me._asLayoutRoot);
        

    );


    var handleAction = function(action)
        Ext.example.msg('<b>Action</b>', 'You clicked "' + action + '"');
    ;

    var colorMenu = Ext.create('Ext.menu.ColorPicker', 
        handler: function(cm, color)
            Ext.example.msg('Color Selected', '<span style="color:#' + color + ';">You choose 0.</span>', color);
        
    );

    var showDate = function(d, value) 
        Ext.example.msg('<b>Action date</b>', 'You picked ' + Ext.Date.format(value, d.format));
    ;

    var fromPicker = false;

    Ext.create('Ext.window.Window', 
        title: 'Standard',
        closable: false,
        height:250,
        width: 500,
        bodyStyle: 'padding:10px',
        contentEl: 'content',
        autoScroll: true,
        tbar: Ext.create('Ext.toolbar.Toolbar', 
            layout: 
                overflowHandler: 'TBar2'
            ,
            items: [
                xtype:'splitbutton',
                text: 'Menu Button',
                iconCls: 'add16',
                handler: Ext.Function.pass(handleAction, 'Menu Button'),
                menu: [text: 'Menu Item 1', handler: Ext.Function.pass(handleAction, 'Menu Item 1')]
            ,'-',
                xtype:'splitbutton',
                text: 'Cut',
                iconCls: 'add16',
                handler: Ext.Function.pass(handleAction, 'Cut'),
                menu: [text: 'Cut menu', handler: Ext.Function.pass(handleAction, 'Cut menu')]
            ,
                text: 'Copy',
                iconCls: 'add16',
                handler: Ext.Function.pass(handleAction, 'Copy')
            ,
                text: 'Paste',
                iconCls: 'add16',
                menu: [text: 'Paste menu', handler: Ext.Function.pass(handleAction, 'Paste menu')]
            ,'-',
                text: 'Format',
                iconCls: 'add16',
                handler: Ext.Function.pass(handleAction, 'Format')
            ,'->', 
                fieldLabel: 'Action',
                labelWidth: 70,
                width: 180,
                xtype: 'datefield',
                labelSeparator: '',
                enableKeyEvents: true,
                listeners: 
                    expand: function()
                        fromPicker = true;
                    ,
                    collapse: function()
                        fromPicker = false;  
                    ,
                    change: function(d, newVal, oldVal) 
                        if (fromPicker || !d.isVisible()) 
                            showDate(d, newVal);
                        
                    ,
                    keypress: 
                        buffer: 500,
                        fn: function(field)
                            var value = field.getValue();
                            if (value !== null && field.isValid()) 
                                showDate(field, value);
                            
                        
                    
                
            , 
                text: 'Sell',
                iconCls: 'money-down',
                enableToggle: true,
                toggleHandler: function(button, pressed) 
                    Ext.example.msg('<b>Action</b>', 'Right ToggleButton ' + (pressed ? 'Buy' : 'Sell'));
                    button.setText(pressed ? 'Buy' : 'Sell')
                    button.setIconCls(pressed ? 'money-up' : 'money-down')
                
            , 
                text: 'Choose a Color',
                menu: colorMenu // <-- submenu by reference
            ]
        )
    ).show();
);

【问题讨论】:

您使用的具体版本是什么? 【参考方案1】:

我们需要为工具栏布局实现overflowHandler,它会添加第二个工具栏,当工具栏的容器宽度不够而无法显示时,将组件从第一个工具栏包装到第二个工具栏。 首先,我们需要重写 Ext.panel.Panel:

Ext4.override(Ext4.panel.Panel, 
    bridgeToolbars: function () 
        var toolbar;
        this.callParent(arguments);
        if (this.tbar2) 
            if (Ext4.isArray(this.tbar2)) 
                toolbar = 
                    xtype: 'toolbar',
                    items: this.tbar2
                ;
             else if (!toolbar.xtype) 
                toolbar.xtype = 'toolbar';
            
            toolbar.dock = 'top';
            toolbar.isTbar2 = true;
            this.dockedItems = this.dockedItems.concat(toolbar);
            this.tbar2 = null;
        
    ,
    onRender: function () 
        this.callParent(arguments);
        var topBars = this.getDockedItems('toolbar[dock="top"]'),
            i,
            len;
        for (i = 0, len = topBars.length; i < len; i++) 
            if (topBars[i].isTbar2) 
                this.tbar2 = topBars[i];
                break;
            
        
    ,
    /**
     * Creates, if not exists, and returns toolbar at passed position
     * @param Ext.panel.Panel panel
     * @param String position
     * @return Ext.toolbar.Toolbar
     */
    getDynamicTBar: function (position) 
        var panel = this,
            params,
            tb;
        position = position || 'top';
        if (position === 'tbar2') 
            tb = panel.tbar2;
            params = 
                dock: 'top',
                isTbar2: true,
                layout: 
                    overflowHandler: 'Scroller'
                
            ;
         else 
            tb = panel.getDockedItems('toolbar[dock="' + position + '"]');
            params = 
                dock: position
            ;
            if (tb.length > 0) 
                tb = tb[0];
            
        
        if (!tb) 
            tb = Ext4.create('Ext4.toolbar.Toolbar', params);
            panel.addDocked(tb);
            if (position === 'tbar2') 
                panel.tbar2 = tb;
            
        
        return tb;
    
);

接下来,我们需要创建 Ext4.layout.container.boxOverflow.TBar2 类(它的名字很长,因为 ExtJS 在硬编码的 Ext4.layout.container.boxOverflow 命名空间中搜索 handleOverflow(字符串的实例))。

/**
 * @class Ext4.layout.container.boxOverflow.TBar2
 * Class for using as overflowHandler
 * @extends Ext.layout.container.boxOverflow.None
 */
Ext4.define('Ext4.layout.container.boxOverflow.TBar2', 
    extend: 'Ext4.layout.container.boxOverflow.None',

    /**
     * @private
     * @property Boolean initialized
     */
    initialized: false,

    constructor: function () 
        /**
         * @private
         * @property Array tbar2Items
         * List of moved components
         */
        this.tbar2Items = [];
        return this.callParent(arguments);
    ,

    beginLayout: function (ownerContext) 
        if (!this.initialized) 
            this.layout.owner.ownerCt.on('afterlayout', this.createTBar2, this);
            this.initialized = true;
        
        this.callParent(arguments);
        this.clearOverflow(ownerContext);
    ,

    beginLayoutCycle: function (ownerContext, firstCycle) 
        this.callParent(arguments);
        if (!firstCycle) 
            this.clearOverflow(ownerContext);
            this.layout.cacheChildItems(ownerContext);
        
    ,

    getOverflowCls: function () 
        return Ext4.baseCSSPrefix + this.layout.direction + '-box-overflow-body';
    ,

    /**
     * @private
     * @property Object _asLayoutRoot
     */
    _asLayoutRoot: 
        isRoot: true
    ,

    clearOverflow: function () 
        // If afterlayout is not processing and tbar2 already created,
        // we can to move components from tbar2 to first toolbar, because now
        // there isn't any overflow
        if (!this.processing && this.tbar2) 
            this.tbar2.items.each(function (item) 
                this.tbar1.add(item);
                // change ui from saved property, or CSS will be incorrect
                item.ui = item.origUI;
            , this);
            this.tbar2.removeAll(false);
            this.tbar2.hide();
        
        this.tbar2Items.length = 0;
    ,

    /**
     * @private
     * Creates tbar2 and does other routines
     */
    createTBar2: function () 
        // if afterlayout event is still processing,
        // or we don't need to handle toolbar overflow, just return from function
        if (this.processing || !this.doTbar2) 
            return;
        
        this.processing = true;
        this.doTbar2 = false;
        var me = this,
            ownerContext = this.mOwnerContext,
            layout = me.layout,
            owner = layout.owner,
            names = layout.getNames(),
            startProp = names.x,
            sizeProp = names.width,
            plan = ownerContext.state.boxPlan,
            available = plan.targetSize[sizeProp],
            childItems = ownerContext.childItems,
            len = childItems.length,
            childContext,
            comp, i, props,
            tbarOwner = owner.ownerCt;

        me.tbar1 = owner;
        // save components which will be moved
        owner.suspendLayouts();
        me.tbar2Items.length = 0;
        for (i = 0; i < len; i++) 
            childContext = childItems[i];
            props = childContext.props;
            if (props[startProp] + props[sizeProp] > available) 
                comp = childContext.target;
                me.tbar2Items.push(comp);
                // save original ui property
                comp.origUI = comp.ui;
                owner.remove(comp, false);
            
        
        owner.resumeLayouts();

        // add tbar2 to the layout cycle (you can see very similar code in clearOverflow
        // method of Ext.layout.container.boxOverflow.Menu)
        tbarOwner.suspendLayouts();
        me.tbar2 = tbarOwner.getDynamicTBar('tbar2');
        me.tbar2.show();
        // moving components
        Ext4.each(me.tbar2Items, function (item, index) 
            me.tbar2.insert(index, item);
            item.ui = item.origUI;
        );
        tbarOwner.resumeLayouts(this._asLayoutRoot);
        this.processing = false;
    ,

    handleOverflow: function (ownerContext) 
        // set flag and store context for later using
        this.doTbar2 = true;
        this.mOwnerContext = ownerContext;
    

);

请注意我使用 ExtJS 4 引导(因为在我的网络应用程序中也有 ExtJD 2.3),所以在你的代码中你可能必须使用 Ext. 而不是 Ext4.

【讨论】:

以上是关于ExtJS 4 中的自定义溢出处理程序实现的主要内容,如果未能解决你的问题,请参考以下文章

ExtJS 4(MVC)商店不使用我的自定义模型类

我应该在哪里定义 ExtJS 4 MVC 中的全局函数?

来自 iOS 锁屏的应用程序中的自定义远程事件处理

spring @Preauthorize 中的自定义方法

GWT 返回从外部 jar 实现接口的自定义对象

React 中的自定义 mailchimp 注册表单