Farewell to Space Consuming Weird Tabs

"I've been trying to push to use the NB platform, and everytime I show a sample, the first thing that is asked is, do you have to have that tab there, it just takes up space and looks weird...", writes Dave at the end of NetBeans Platform: Also For Small Applications?

Another problem is that you can right-click on those tabs (even when you've removed the "x", via a setting in the etc/foo.conf file) and click "Close", thus closing the tab, even when you wouldn't want the user to be able to do so.

Well, Dave and others, take a look at this:

Now there's no tab, right? Compare it to the one in NetBeans Platform: Also For Small Applications?, and you'll see the difference.

To paraphrase Tim Boudreau in a recent thread on dev@openide.netbeans.org, Swing controls have a notion of a "UI delegate" in other words, a Swing component isn't responsible for painting itself. Instead, there is an object that a Swing component asks "please paint me" when it wants to be painted. Same for mouse and keyboard handling. Change the UI delegate and you get a different look or behavior. So this is what makes pluggable look and feels possible. The tab control in NetBeans uses the exact same mechanisms for its appearance and behavior. So, if you replace the UI delegate (via Class TabDisplayerUI), you can have different appearance and behavior, including having no tabs at all.

The steps to take in making it happen are as follows:

  1. Use the Module Installer wizard to create... a module installer. You don't only get a class that extends ModuleInstall, but an entry is added to the Manifest file and a dependency is declared in the project.xml file. A module installer is a Java class that provides hooks for running code on startup or when a module is loaded. It can also run cleanup code when a module is uninstalled or disabled. As a free gift, NetBeans gives you a skeleton restored() method. Fill it out as follows:

    public void restored() {
       UIManager.put("EditorTabDisplayerUI", "org.yourorghere.NoTabsTabDisplayerUI"); 
    }

    What is very important is that you set the appropriate values here (duh). Firstly, "EditorTabDisplayerUI" implies that the TopComponents that you're interested in will be positioned in the "editor" position (in the IDE or in your own application), which is something you can change manually (very quickly and easily) in the layer.xml file. If you wanted to remove the tabs from positions such as output, explorer, and properties, you would use ViewTabDisplayerUI instead. There are also other values you could put there, but the only one I'm interested in is the one that affects the editor position. It is pretty cool that not ALL the tabs will be removed, because I think there are many scenarios where one would ONLY want the editor position to be affected, as suggested by Ramón Ramos here.

    Secondly, the org.yourorghere.NoTabsTabDisplayerUI should point to the class that contains the information for overriding your tabs. Step 3 below describes this class.

  2. Set up your module to depend on Tab Control, Utilities API, Module System API, and Look & Feel Customization Library.

  3. Create a Java class called NoTabsTabDisplayerUI.java, which will replace the standard UI delegate for editor tabs with one that is 0 size and does nothing:

    public class NoTabsTabDisplayerUI extends TabDisplayerUI {
        
        /\*\* Creates a new instance of NoTabsTabDisplayerUI \*/
        public NoTabsTabDisplayerUI(TabDisplayer displayer) {
            super(displayer);
        }
        
        public static ComponentUI createUI(JComponent jc) {
            assert jc instanceof TabDisplayer;
            return new NoTabsTabDisplayerUI((TabDisplayer) jc);
        }
        
        private static final int[] PTS = new int[] { 0, 0, 0 };
        public Polygon getExactTabIndication(int i) {
            //Should never be called
            return new Polygon(PTS, PTS, PTS.length);
        }
        
        public Polygon getInsertTabIndication(int i) {
            return new Polygon(PTS, PTS, PTS.length);
        }
        
        public int tabForCoordinate(Point point) {
            return -1;
        }
        
        public Rectangle getTabRect(int i, Rectangle rectangle) {
            return new Rectangle(0,0,0,0);
        }
        
        protected SingleSelectionModel createSelectionModel() {
            return new DefaultSingleSelectionModel();
        }
        
        public java.lang.String getCommandAtPoint(Point point) {
            return null;
        }
        
        public int dropIndexOfPoint(Point point) {
            return -1;
        }
        
        public void registerShortcuts(javax.swing.JComponent jComponent) {
            //do nothing
        }
        
        public void unregisterShortcuts(javax.swing.JComponent jComponent) {
            //do nothing
        }
        
        protected void requestAttention(int i) {
            //do nothing
        }
        
        protected void cancelRequestAttention(int i) {
            //do nothing
        }
        
        public Dimension getPreferredSize(javax.swing.JComponent c) {
            return new Dimension(0, 0);
        }
        
        public Dimension getMinimumSize(javax.swing.JComponent c) {
            return new Dimension(0, 0);
        }
        
        public Dimension getMaximumSize(javax.swing.JComponent c) {
            return new Dimension(0, 0);
        }
    }

This may seem like a bit of work and in some ways it is. It would be nice if I could set some sort of property that excludes tabs from TopComponents in one or more positions. However, until such a property (or a simpler approach) is available, it is at least possible to remove those tabs when they're not needed.

Comments:

That is very cool Geertjan, looks like I've got some new work and meetings to have now. :-) I'll run some prototype tests to see what, if any problems I come up with. Thanks, and awesome job! Dave

Posted by Dave on June 08, 2006 at 12:35 AM PDT #

Hi Dave, very glad to hear it! Actually, everything in this blog entry comes straight from one of Tim Boudreau's responses on (a very useful mailing list) dev@openide.netbeans.org. Here it is:

http://openide.netbeans.org/servlets/ReadMsg?list=dev&msgNo=21187

Good luck with your meetings and, should you have any other questions or whatever, just drop a line.

Posted by Geertjan on June 08, 2006 at 01:11 AM PDT #

I just thought of a possible improvement. You might want to create a module that could either live as a platform app or as a module in the IDE. You'd only want to hide the tab in the case of a platform app. Therefore, you'd want to wrap your UIManager.put() call in an if statement. Not sure exectly what the condition would be. Perhaps you can get a list of installed clusters and look for one that begins with the string "ide".

Posted by Rich Unger on June 09, 2006 at 02:09 AM PDT #

Cool idea. For your if statement, how about this:

if (loud.Scream.Of.Annoyance == true {
UIManager.put("EditorTabDisplayerUI", "org.yourorghere.NoTabsTabDisplayerUI");
}

Posted by Geertjan on June 09, 2006 at 02:16 AM PDT #

Yeah, write about it again when you get rid of that ugly ass three sided border and top separator.

Posted by Cynic on June 30, 2006 at 04:06 AM PDT #

The border used for tab components is also settable via a UIManager key, so you can get rid of the "ugly ass three sided border and top separator" too. Note that there are \*two\* borders - one is for the component that holds the contents, one is for the tab displayer component (TabDisplayer can actually be used as a component in its own right that just displays a set of tabs and has a SingleSelectionModel).

Posted by Tim Boudreau on July 04, 2006 at 03:36 AM PDT #

That's cool! I was wondering for some time how I could get rid of this lone tab in the "editor" position. But now how can I maximize/restore this TopComponent programmatically (through an action for example)? I have searched through NetBeans sources and have found that this action was handled deep inside the look'n feel classes of the tabbed component.

Posted by Hervé on July 17, 2006 at 03:47 AM PDT #

So your only choices are 'editor' or 'everything else including custom modes' ? I want tabs (well, real tabs like the old output tabs, not the new ones) in the output mode, but no tabs in my other custom modes. Any idea where the code is that makes this "editor/other" mode choice might be?

Posted by Roxie on August 04, 2006 at 09:54 AM PDT #

There is still the status bar in the bottom of the window to be removed, in order to have the same UI.

Posted by Vincent on October 01, 2007 at 01:47 PM PDT #

... and an horizontal scroll bar as well.

Posted by Vincent on October 01, 2007 at 01:48 PM PDT #

I can't find the key to get rid of the "top separator". Does anybody know which one it is?

Posted by Javier Molina on October 30, 2007 at 09:04 PM PDT #

Rather than removing tabs, I'd like to use this method for changing the font size that appears on the tabs.

I've had a look at the documenation and I think I can use the getTxtFont() method in TabDisplayerUI.

I'm new to Java though and would like to know what the original TabDisplayerUI looked like before the changes were made to remove the tabs.

I can then add a getTxtFont() to this and hopefully solve my problems.

Do you know where I can find this?

Thanks

Posted by Alastair Badman on January 21, 2009 at 02:40 AM PST #

Try adding:
UIManager.put(DefaultTabbedContainerUI.KEY_EDITOR_CONTENT_BORDER,BorderFactory.createEmptyBorder());
to remove the 3 sided border.

Posted by Michael b on April 27, 2009 at 09:59 AM PDT #

Here my solution to remove TabDisplayer or just the navigation buttons on a per-TopComponent basis:

public static void hideTabDisplayer(final TopComponent topComponent) {
WindowManager.getDefault().invokeWhenUIReady(
new Runnable() {
@Override
public void run() {
try {
Container tabbedContainer = UIUtilities.findAncestorByClassName(topComponent, TabbedContainer.class.getName());
Component tabDisplayer = UIUtilities.findComponentByClassName(tabbedContainer, TabDisplayer.class.getName());
tabDisplayer.setVisible(false);
} catch (Exception ex) {
}
}
});
}

public static void hideTabNavigationButtons(final TopComponent topComponent) {
WindowManager.getDefault().invokeWhenUIReady(
new Runnable() {
@Override
public void run() {
try {
Container tabbedContainer = UIUtilities.findAncestorByClassName(topComponent, TabbedContainer.class.getName());
Component dropDownButton = UIUtilities.findComponentByClassName(tabbedContainer, TabControlButtonFactory.class.getName() + "$DropDownButton");
dropDownButton.getParent().setVisible(false);
} catch (Exception ex) {
}
}
});
}

Last, if tab must be preserved but the popup menu removed, just override TopComponent.getActions() to return null.

Posted by guest on July 21, 2011 at 06:52 PM PDT #

Sorry for digging up this older post, but it kind of helped and kind of totally messed things up... :D

It definitely hides the tab-bar.
Now let me explain my problem: my program has a wizard/dialog-structure, which means, you see one window, click a few things, click on "next" and a totally different window pops up.
The designer didn't allow to take the builtin Netbeans-RCP-Wizard-feature, so I did it this way: every time I click on next, the current TopComponent gets .close() and another one is .open[ed](). This solution works perfectly fine - until I want to hide my tabs.

Now do you know, if that code has stolen some functionality in opening new TopComponents in the Editor?

For me the following happens: the last seen TopComp loads fine but if I hit "next" or "back", instead of the next or previous TopComp I get an empty one and no Java-Exception.

Posted by guest on October 27, 2011 at 02:11 PM PDT #

I noticed that the deploy of a netbeans platform application is very 'expensive' in terms of disk space and in packaging.

I tried deploy my simple application wich contains a simple topcomponent, no extra modules (just a few dependencies with jfreechart adn javolution), no lookapi api, no need of extra tabs or special features of the ui. Just one window with a jfreechart and some bujttons/textboex/comboboxes. When I deploy the aspplication via Package as ->ZIP distribution, and then I unpack the zip file on the production machine, the content of the folder is more then 30MB!!! In particular the 'platform' subdirectory is 29MB!!! Is there a way to compact the packaging? I am sure I'm using a lot of stuff no need in my case.

Then, there is also another stuff to say. My application is just one small piece of all the system I need to provide. Mine is a complete suite of programs, some in C/C++, some in Java command line, some other in java desktop application (like this one) etc. I have my main directory with my structure. I jave a structure like this:

<installdir>
- bin
- etc
- log
- <some other>
- <some other other>

I have all my executables in the bin directory. I have both C/C++ programs and Java program. For example there is a command line program in java called jobmgr.jar in the bin directory. I simply run it using 'java -jar <installdir>/bin/jobmgr.jar -config <installdir>/etc/jobmgr.config' and it works perfectly well. The 'bin' directory has a 'lib' subdirectory which contains all the dependencies of the jobmgr.jar program. I have some other tasks written in java, put in the 'bin' directory. I use the same 'lib' subdirectory to contains dependencies of all the java programs so that they can share everything. The deploy of a netbenas platform application acts like if it was the one and only program and creates a directory structure that can't be used in the scenario I described above. How can I fit the deploy of a netbeans platform appllication in such a scenario? Please help.

Posted by Magallo on March 05, 2013 at 02:04 AM PST #

Not sure how any of this relates to this blog entry. Why not write to the dev@platform.netbeans.org mailing list with these questions? You'll start a discussion with many people, instead of just with me.

Posted by Geertjan on March 05, 2013 at 02:20 AM PST #

PS: Here's the most minimal NetBeans Platform application, but you know this tutorial since you must have worked through the NetBeans Platform Learning Trail already:

http://platform.netbeans.org/tutorials/nbm-runtime-container.html

Posted by Geertjan on March 05, 2013 at 02:22 AM PST #

PS: If you're not on the mailing list yet, here's how to join it, otherwise you can't send mails there:

http://netbeans.org/projects/platform/lists/dev/archive

Posted by Geertjan on March 05, 2013 at 02:51 AM PST #

Ok, sorry for the off topic. Anyway I think this is a topic that should be faced because it frighten a lot of people I guess.

Posted by Magallo on March 05, 2013 at 03:13 AM PST #

No, it doesn't frighten anyone (see hundreds of applications here: http://platform.netbeans.org/screenshots.html), because what you're saying isn't true. That's what I'm trying to tell you. Did you follow the tutorial above? It shows you a minimal NetBeans Platform application. Also, most applications on the NetBeans Platform are large anyway, that's the purpose of the NetBeans Platform: to offer a framework for large applications. Some small ones too, but typically large ones. But why don't we have this discussion on the mailing list instead of here?

Posted by Geertjan on March 05, 2013 at 03:21 AM PST #

Does this still work for Netbeans 7.2 and Java 1.7? I have been trying this with my application and it does not work.

Posted by guest on June 27, 2013 at 12:15 PM PDT #

Put the code in the 'restored()' method into a SwingUtilities.invokeLater.

Posted by Geertjan on June 27, 2013 at 03:59 PM PDT #

I modified the contents of the restored() function to look like this:

public void restored() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
UIManager.put("EditorTabDisplayerUI", "org.foo.NoTabsTabDisplayerUI");
}
});
}

Is that right? I'm still getting tabs...

Posted by guest on June 28, 2013 at 05:28 AM PDT #

Oops, looks like I spoke too soon. It was an interaction with another module in the application that was preventing it from working. Thanks for your help!

Posted by guest on June 28, 2013 at 06:21 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today