1. #21
    Ext User Matti's Avatar
    Join Date
    Jul 2008
    Location
    Belgium
    Posts
    9
    Vote Rating
    0
    Matti is on a distinguished road

      0  

    Default


    Quote Originally Posted by mholyszko View Post
    Thank you very much, Matti, tabs d&d works correctly now!

    Now if you could just look on the "tab with icon" issue, which I described in my post above: http://extjs.com/forum/showthread.ph...810#post265810

    Once again I would like to thank you for this great extension and effort you put in making it even better.
    The problem here is the ExtJS CSS. On line 505 in ext-all.css, you'll find:
    Code:
    .x-tree-node a span, .x-dd-drag-ghost a span {
        text-decoration:none;
        color:black;
        padding:1px 3px 1px 2px;
    }
    Now, the problem here is that .x-dd-drag-ghost a span has a CSS specificity of 12 (1 class selector + 2 tag selectors). Therefore, if you declare your icon class like...
    Code:
    .icon-home {
        background-image:url(/path/to/home.gif);
        padding-left: 20px;
    }
    ...the left padding won't be applied since .icon-home has a specificity of only 10 (a single class selector).

    So, in order to make the left padding applied to the drag and drop proxy, you'll need to make your selector have a specificity greater than 12. For my site, I use
    Code:
    .x-dd-drag-ghost a span.icon-home {
        padding-left: 20px;
    }
    which has a specificity of 22 (2 class selectors + 2 tag selectors) and therefore it can override the padding. You could also use !important, however that could cause trouble with older browsers.

    Well, I don't know it's possible to do this through code. You could try to give the proxy a style attribute which always overrides the selector styles, but then you first need to know whether the dragged element has an iconCls... I think it's possible, I'll have a look at it...

    EDIT: Okay, good news. I found a quite simple way to do this without extra CSS needed. I managed to detect the iconCls and to select the span in the drag proxy. Version 1.0.5 is now available through the SVN.
    Last edited by Matti; 10 Jan 2009 at 8:18 AM. Reason: Success!

  2. #22
    Ext User
    Join Date
    Mar 2008
    Location
    Poland
    Posts
    17
    Vote Rating
    0
    mholyszko is on a distinguished road

      0  

    Default


    Thank you very much, it works flawlessly now

  3. #23
    Sencha User Remy's Avatar
    Join Date
    Apr 2008
    Posts
    298
    Vote Rating
    0
    Remy is on a distinguished road

      0  

    Default


    Quote Originally Posted by Matti View Post
    ...the left padding won't be applied since .icon-home has a specificity of only 1 (a single class selector).
    I know its not terribly important but in the interest of learning, the above should be specificity of 10 rather than 1. Just a typo but I hadn't even heard of this term until you pointed it out. Thanks.

    Remy

  4. #24
    Sencha - Community Support Team mystix's Avatar
    Join Date
    Mar 2007
    Location
    Singapore
    Posts
    6,236
    Vote Rating
    5
    mystix will become famous soon enough

      0  

    Thumbs up


    some minor observations:
    • the drop indicator shouldn't appear on the immediate left / right of the tab being moved (refer to Firefox's / Opera's tab dragging behaviour).
    • when enableTabScroll = true, dragging a tab to the extreme right of the viewport when tab scrollers are present results in some real funky behaviour. set scroll = false on the dragsource to prevent this
      i.e.
      Code:
      initTab: function(tp, item) {
      // ... [snip] ...
      
              tab.ds = new Ext.dd.DragSource(id, {
                  ddGroup: tabsDDGroup,
                  dropEl: tab,
                  dropElHeader: Ext.get(id, true),
                  scroll: false
              });
      
      // ... [snip] ...
      }
    • some event / dom cleanup is required (i suggest moving the stuff in initEvents() to afterRender() instead since there's no event-related stuff in DDTabPanel's initEvents() method):
      Code:
      Ext.override(Ext.ux.panel.DDTabPanel, {    
          afterRender: function() {
              Ext.ux.panel.DDTabPanel.superclass.afterRender.call(this);
      
              // Create a drop arrow indicator
              this.arrow = Ext.DomHelper.append(
                  Ext.getBody(),
                  '<div class="dd-arrow-down dd-arrow-down-invisible"></div>',
                  true
              );
      
              // Create a drop target for this tab panel
              this.dd = new Ext.ux.panel.DDTabPanel.DropTarget(this, {
                  ddGroup: this.ddGroupId
              });    
          },
          
          // initEvents: function() { /* ... */ } // moved everything to afterRender()
          
          // dragsource cleanup on removed tabs
          onRemove: function(tp, item) {
              Ext.destroy(item.ds.proxy, item.ds);
              
              Ext.ux.panel.DDTabPanel.superclass.onRemove.call(this, tp, item);
          },
          
          // droptarget + arrow cleanup
          onDestroy: function() {
              Ext.destroy(this.dd, this.arrow);
          
              Ext.ux.panel.DDTabPanel.superclass.onDestroy.call(this);        
          }
      });
    • why incur the overhead of creating a new Ext.Element() when you already have a reference which is an Ext.Element?
      e.g. in notifyDrop():
      Code:
                  var tab = tabs.itemAt(i),
                      tabDom = tab.ds.dropElHeader.dom, // Is this tab target of the drop operation?
                      tabLeft = new Ext.Element(tabDom).getX(), // Getting the absolute X coordinate of the tab
                      tabRight = tabLeft + tabDom.clientWidth,
                      tabMiddle = tabLeft + tabDom.clientWidth / 2; // Get the middle of the tab
      versus simply
      Code:
                  var tab = tabs.itemAt(i),
                      tabEl = tab.ds.dropElHeader, // Is this tab target of the drop operation?
                      tabLeft = tabEl.getX(), // Getting the absolute X coordinate of the tab
                      tabRight = tabLeft + tabEl.dom.clientWidth,
                      tabMiddle = tabLeft + tabEl.dom.clientWidth / 2; // Get the middle of the tab
    • moving the call to applyStyle() from Ext.ux.panel.DDTabPanel.DropTarget.notifyOver() to Ext.ux.panel.DDTabPanel.initTab() improves drag proxy performance
      Code:
          // init the drag source after (!) rendering the tab
          initTab: function(tab, index) {
      
      // ... [SNIP]
      
              tab.ds.onStartDrag = function() {
                  if (this.dropEl.iconCls) {
                      this.getProxy().getGhost().select(".x-tab-strip-text").applyStyles({
                          paddingLeft: "20px" // Add left padding if the tab has an iconCls
                      });
                  }
              };
      
      // ... [SNIP] ...
    • the Ext.ux.panel.DDTabPanel.DropTarget constructor can be simplified to
      Code:
          constructor: function(tabpanel, config) {
              this.tabpanel = tabpanel;
      
              // var target = Ext.select('div.x-tab-panel-header', false, tabpanel.getEl().dom).elements[0];
      
              // The drop target is the tab strip wrap
              Ext.ux.panel.DDTabPanel.DropTarget.superclass.constructor.call(this, tabpanel.stripWrap, config);
          },
      which should also enable drag / drop on tabPosition:'bottom' tabpanels
    • in complex layouts, the following modified check in Ext.ux.panel.DDTabPanel.DropTarget.notifyOver() will improve drag proxy performance:
      Code:
      // ... [SNIP]
      
              if (last < 2 || !e.within(this.getEl())) {
                  return 'x-dd-drop-nodrop';
              }
      
      // ... [SNIP]
    • the dd-arrow-down-invisible css class is unnecessary. simply calling show() / hide() on the arrow element will achieve the same effect. e.g.:
      Code:
      notifyDrop: function() {
          // ... [SNIP]
          
          // this.tabpanel.arrow.addClass('dd-arrow-down-invisible');
          this.tabpanel.arrow.hide();
      
          // ... [SNIP]
      }
      
      notifyOver: function(dd, e, data){
          // ... [SNIP]
          
          // larrow.removeClass('dd-arrow-down-invisible');
          larrow.show();
          
          // ... [SNIP]
      }


    great job!
    Last edited by mystix; 6 Jan 2009 at 1:41 AM. Reason: more observations

  5. #25
    Sencha - Services Team arthurakay's Avatar
    Join Date
    Sep 2008
    Location
    Antioch, IL
    Posts
    1,352
    Vote Rating
    32
    arthurakay is a jewel in the rough arthurakay is a jewel in the rough arthurakay is a jewel in the rough

      0  

    Default Possible Bug?

    Possible Bug?


    I'm noticing that in Opera 9.63, the right-click menus on the tabs don't work. Opera's own right-click menus show (for opening new tabs, etc).

    I didn't see that listed anywhere as a bug or problem, so I thought I'd point it out. So far, this is the only browser in which I see the issue.
    Arthur Kay
    Developer Relations Manager, Sencha Inc.

    Twitter | Sencha Chicago User Group

  6. #26
    Sencha - Community Support Team mystix's Avatar
    Join Date
    Mar 2007
    Location
    Singapore
    Posts
    6,236
    Vote Rating
    5
    mystix will become famous soon enough

      0  

    Default


    Quote Originally Posted by arthurakay View Post
    I'm noticing that in Opera 9.63, the right-click menus on the tabs don't work. Opera's own right-click menus show (for opening new tabs, etc).

    I didn't see that listed anywhere as a bug or problem, so I thought I'd point it out. So far, this is the only browser in which I see the issue.
    it's impossible to disable the built-in right-click context menu since Opera 8.x

  7. #27
    Ext User Matti's Avatar
    Join Date
    Jul 2008
    Location
    Belgium
    Posts
    9
    Vote Rating
    0
    Matti is on a distinguished road

      0  

    Default


    Thank you for the great suggestions and code improvements, mystix! I really appreciate it!

    I implemented almost all of your suggestions because they really make the code simpler, better and faster. The only thing which I didn't change was your first suggestion:
    Quote Originally Posted by mystix View Post
    • the drop indicator shouldn't appear on the immediate left / right of the tab being moved (refer to Firefox's / Opera's tab dragging behaviour).
    Actually, Firefox does show an arrow next to the dragged tab. IE7 does the same thing, however I don't know about Opera as I have yet to install it. In my opinion, this is a good thing: it's logical that when you drag the tab to the left of the tab at the right, it shows an arrow but doesn't move.
    Or, to say it with a quote:
    "That's not a bug, that's an unexpected feature!"

    Anyway, check the first post to get version 1.0.6.

  8. #28
    Sencha - Community Support Team mystix's Avatar
    Join Date
    Mar 2007
    Location
    Singapore
    Posts
    6,236
    Vote Rating
    5
    mystix will become famous soon enough

      0  

    Default


    found a few more spots in v1.0.6 where redundant code can be removed:
    Code:
    Ext.override(Ext.ux.panel.DDTabPanel.DropTarget, {
        notifyOver: function(dd, e, data) {
            this.tabpanel = dd.dropEl.ownerCt; // this.tabpanel is already set in the constructor
    
            // .... [SNIP] ...
    
            // Getting the absolute Y coordinate of the tabpanel
            var panelDom = new Ext.Element(this.el.dom);
            var tabPanelTop = panelDom.getY();
            var tabPanelTop = this.el.getY(); // this.el is known, so just get its Y-coordinate
    
            // ... [SNIP] ...
    
            larrow.setTop(tabPanelTop + tp.arrowOffsetY);
            larrow.setLeft(left + tp.arrowOffsetX);
            larrow.show();
            larrow.setTop(tabPanelTop + this.tabpanel.arrowOffsetY).setLeft(left + this.tabpanel.arrowOffsetX).show(); // chain function calls, just because we can =)
    
            return 'x-dd-drop-ok';        
        },
    
        notifyDrop: function(dd, e, data) {
            this.tabpanel = dd.dropEl.ownerCt; // this.tabpanel is already set in the constructor
    
            // ... [SNIP] ...
        },
    
        notifyOut: function(dd, e, data) {
            this.tabpanel = dd.dropEl.ownerCt; // this.tabpanel is already set in the constructor
            this.tabpanel.arrow.hide();
        }
    });
    i also made the following changes to Ext.ux.panel.DDTabPanel:
    • minor refactor using Ext.apply (just thought it looks neater) in the Ext.ux.panel.DDTabPanel.initTab()
    • removed the beforeDragEnter() method -- it's not required for tab activation
    • activate tab in the onMouseDown() method by simply calling show() on this.dropEl
    Code:
    Ext.override(Ext.ux.panel.DDTabPanel, {
        // init the drag source after (!) rendering the tab
        /** @private */
        initTab: function(tab, index) {
            Ext.ux.panel.DDTabPanel.superclass.initTab.call(this, tab, index);
    
            var id = this.id + '__' + tab.id;
            var tabsDDGroup = this.ddGroupId;
    
            // default: enable drag on all tabs
            Ext.applyIf(tab, { allowDrag: true });
    
            Ext.apply(tab, {
                // Set the initial tab position
                position: (index + 1) * 2, // 2, 4, 6, 8, ... (2n)
    
                // Make this tab a drag source
                ds: new Ext.dd.DragSource(id, {
                    ddGroup: this.ddGroupId,
                    dropEl: tab,
                    dropElHeader: Ext.get(id, true),
                    scroll: false,
    
                    // update drag proxy ghost element
                    onStartDrag: function() {
                        if (this.dropEl.iconCls) {
                            this.getProxy().getGhost().select(".x-tab-strip-text").applyStyles({
                                paddingLeft: "20px" // Add left padding if the tab has an iconCls
                            });
                        }
                    },
    
                    // Activate this tab before starting the drag action // removed redundant onMouseDown() method
                    beforeDragEnter: function(target, event, id) {
                        target.tabpanel.activate(this.dropEl);
                        this.dropEl.show();
                    },
    
                    // Activate this tab on mouse down
                    // (Fixed bug which prevents a tab from being activated by clicking it)
                    onMouseDown: function(event) {
                        if (!this.dropEl.isVisible()) {
                            this.dropEl.show(); // simply call the tab's show() method to activate it
                        }
                    }
                }),
    
                // Method to enable dragging
                enableDrag: function() {
                    this.allowDrag = true;
                    return this.ds.unlock();
                },
    
                // Method to disable dragging
                disableDrag: function() {
                    this.allowDrag = false;
                    return this.ds.lock();
                }
            });
    
            // Initial dragging state
            if (tab.allowDrag) {
                tab.enableDrag();
            } else {
                tab.disableDrag();
            }
        }
    });
    Last edited by mystix; 11 Jan 2009 at 9:06 PM. Reason: update

  9. #29
    Sencha Premium Member
    Join Date
    Jun 2008
    Posts
    321
    Vote Rating
    3
    Scorpie is on a distinguished road

      0  

    Default


    Wow, nice work mystix!
    I`m from Holland!

  10. #30
    Sencha User Mjollnir26's Avatar
    Join Date
    Oct 2008
    Location
    Germany
    Posts
    152
    Vote Rating
    0
    Mjollnir26 is on a distinguished road

      0  

    Default


    Great thing, will help to make my App even sexier
    Thanks very much!