Frameless (undecorated) figure windows

All Matlab figures have a standard frame around them, consisting of a border and title bar. In some cases it could be useful to present a figure window, displaying only the contents without the border and title bar. Such a borderless (undecorated) window is not possible in Matlab. Well, at least not in supported/documented Matlab…

Readers of this blog and/or my Matlab-Java programming book are aware that Matlab’s GUI is based on Java Swing. In the end, every Matlab figure window is a simple Java JFrame, and JFrames have a setUndecorated(true) method that can be called to remove the border and title bar.

An undecorated Matlab figure window
An undecorated Matlab figure window

Attempt #1 – direct invocation

Unfortunately, we cannot directly call setUndecorated in Matlab, because it only works when the JFrame has not yet been displayed:

% Get the figure's underlying Java JFrame reference handle
>> mjf = get(handle(gcf), 'JavaFrame');
>> jWindow = mjf.fHG2Client.getWindow  % or: mjf.getAxisComponent.getTopLevelAncestor
jWindow =
com.mathworks.hg.peer.FigureFrameProxy$FigureFrame[fClientProxyFrame,740,-761,576x509,...]
 
% Try to remove decoration
>> jWindow.setUndecorated(true)
Java exception occurred:
java.awt.IllegalComponentStateException: The frame is displayable.
	at java.awt.Frame.setUndecorated(Unknown Source)

On the other hand, if we try to call setUndecorated on a new invisible figure then we’d see that the JFrame component is not yet created by Matlab:

% Create a simple Matlab figure
hFig = figure('Name','Plot example', 'Visible','off');
 
% Get the underlying Java JFrame reference handle (empty)
>> mjf = get(handle(hFig),'JavaFrame');
>> jWindow = mjf.getAxisComponent.getTopLevelAncestor
jWindow =
     []
>> jWindow = mjf.fHG2Client.getWindow
jWindow =
     []
 
>> jWindow.setUndecorated(true)
Attempt to reference field of non-structure array.

So we have a catch-22 situation: we can’t call setUndecorated until the JFrame is created, but Matlab only creates it when it displays the figure window, and then it’s too late to undecorate…

Attempt #2 – reparenting: an optical illusion

One way that I found around this problem is to reparent the Matlab JFrame‘s content onto a pure Java JFrame that has been made undecorated. To hide the Matlab figure after creating the JFrame, we can simply move the figure’s position to be outside the visible monitor area:

% Create a simple Matlab figure (visible, but outside monitor area)
t = 0 : 0.01 : 10;
hFig = figure('Name','Plot example', 'ToolBar','none', 'MenuBar','none');
hLine = plot(t, cos(t));
hButton = uicontrol('String','Close', 'Position',[307,0,45,16]);
 
% Ensure that everything is rendered, otherwise the following will fail
drawnow;
 
% Get the underlying Java JFrame reference handle
mjf = get(handle(hFig), 'JavaFrame');
jWindow = mjf.fHG2Client.getWindow;  % or: mjf.getAxisComponent.getTopLevelAncestor
 
% Get the content pane's handle
mjc = jWindow.getContentPane;
mjr = jWindow.getRootPane;  % used for the offset below
 
% Create a new pure-Java undecorated JFrame
figTitle = jWindow.getTitle;
jFrame = javaObjectEDT(javax.swing.JFrame(figTitle));
jFrame.setUndecorated(true);
 
% Move the JFrame's on-screen location just on top of the original
jFrame.setLocation(mjc.getLocationOnScreen);
 
% Set the JFrame's size to the Matlab figure's content size
%jFrame.setSize(mjc.getSize);  % slightly incorrect by root-pane's offset
jFrame.setSize(mjc.getWidth+mjr.getX, mjc.getHeight+mjr.getY);
 
% Reparent (move) the contents from the Matlab JFrame to the new JFrame
jFrame.setContentPane(mjc);
 
% Make the new JFrame visible
jFrame.setVisible(true);
 
% Hide the Matlab figure by moving it off-screen
pos = get(hFig,'Position');
set(hFig, 'Position',[-1000,-1000,pos(3:4)]);
drawnow;

Matlab figures are not pure JFrame but rather a subclass (com.mathworks.widgets.desk.DTSingleClientFrame). So instead of creating a new JFrame, we could create a new instance of Matlab’s class instead, potentially solving a few problems:

jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
jFrame = javaObjectEDT(com.mathworks.widgets.desk.DTSingleClientFrame(jDesktop, figTitle));
% ...the rest is exactly the same as above...

Either way, we now get a nice undecorated window containing our Matlab contents (see screenshot above).

Once the undecorated JFrame becomes visible, we should not hide or delete the original Matlab figure, because this will stop rendering of the contents.

Working with the undecorated frame

We can modify the Matlab figure and its contents normally, just as if they still appeared within the original Matlab figure. This can be used to display a dynamic application splash-screen, that displays information on various initialization/loading steps. Or alternately we could use it to display a system health-monitor (small panel with red/green indicators), or maybe a graph with streaming market data or live news alerts. The usages are endless, limited only by your imagination, not by Matlab.

For example, let’s modify our plot dynamically using a timer, and set the close button’s callback to dismiss the new JFrame and original figure:

% Set a timer to dynamically update the plot every 0.1 sec
start(timer('TimerFcn', {@timerCallback,hLine}, 'ExecutionMode','fixedRate', 'Period',0.1));
 
% Set the close button callback
set(hButton, 'Callback',{@closeCallback,jFrame});
 
% This is the close button's callback
function closeCallback(hButton, eventData, jFrame)
   delete(ancestor(hButton,'figure'));
   dispose(jFrame);
end
 
% This is the plot timer's callback
function timerCallback(hTimer, eventData, hLine)
   xdata = get(hLine,'XData') + 0.1;
   set(hLine, 'XData',xdata, 'YData',cos(xdata));
   xlim([min(xdata) max(xdata)]);
end

Note: if you don’t hide the toolbar/menubar in the original Matlab figure, some of their functions will not work properly in the new JFrame (e.g., zoom, pan etc.). But in most use-cases we do not want a toolbar/menubar in an undecorated window. The example above showed an undecorated window without the toolbar/menubar.

If we wish to avoid having the new JFrame appear in the Operating System’s taskbar, we can use the following command:

jFrame.setType(javaMethod('valueOf','java.awt.Window$Type','UTILITY'))

Note that this command must be executed before the JFrame is made visible. Also note that it only works with Java 7, in other words Matlab R2013b (8.2) or newer (or if you are very adventurous and happen to use an older Matlab with a custom Java 7 installation).

But now that there is no taskbar component, how can we transfer focus to our new undecorated window? In other words, if another windows hides our new undecorated frame, how can we get it back on top?

A simple solution is to set the Matlab figure frame’s FocusGainedCallback to requestFocus for the newly created jFrame. Then, when we click the Matlab figure’s button on the taskbar, the new jFrame will pop into focus. In effect, this provides the optical illusion that the taskbar button refers to the undecorated window (since the actual Matlab figure is positioned beyond the monitor’s area):

hjWindow = handle(jWindow, 'CallbackProperties');
set(hjWindow, 'FocusGainedCallback', @(h,e)jFrame.requestFocus);

Likewise, we should instrument our Matlab figure so that when it is closed/deleted, so too is our new jFrame:

set(hjWindow, 'WindowClosedCallback', @(h,e)jFrame_.dispose);

undecorateFig and redecorateFig

I have encapsulated all of the above in a couple of very easy-to-use utilities that I just posted on the Matlab File Exchange: undecorateFig, redecorateFig. Using them can’t get any simpler than this:

undecorateFig;        % undecorate the current figure (gcf)
undecorateFig(hFig);  % hFig is any GUI handle (needs to be visible)
 
redecorateFig;        % redecorate the current figure (gcf)
redecorateFig(hFig);  % hFig is any GUI handle

Any sufficiently advanced technology is indistinguishable from magic – Arthur C. Clarke   :-)

Have you made some interesting use of this nice feature in your application? If so, please share it in a comment below.

A suggested related mini project for anyone interested: add an image reflection of the current figure contents beneath the figure. You could use my ScreenCapture utility or directly use java.awt.Robot.createScreenCapture(mjc.getLocationOnScreen), process the resulting image (blur, apply transparency gradient, crop at 50% height etc.) and place the resulting image within an undecorated JFrame placed directly beneath the main figure. You can instrument the figure (hjWindow)’s ComponentMovedCallback and ComponentResizedCallback to move/resize the reflection JFrame whenever the parent figure moves or is resized. You could use a timer to periodically update the reflection image so that it remains synchronized with the parent. You’ll probably also want to add a nice toggle-button to the figure’s toolbar to turn the reflection on/off. Maybe I’ll hack this project someday when I have some spare time. Or maybe someone will beat me to it… Care to try?

Carlos David Peña liked this post
Categories: Figure window, GUI, High risk of breaking in future versions, Java, Undocumented feature

Tags: , , , ,

Bookmark and SharePrint Print

12 Responses to Frameless (undecorated) figure windows

  1. David says:

    Hi Yair,

    Great post and very useful to know but isn’t this type of figure modification in breach of MathWorks license agreement?

    http://www.mathworks.com/matlabcentral/answers/91579-is-it-possible-to-control-the-appearance-of-the-title-bar-and-the-title-bar-icon-of-figure-windows-i

    • Yair Altman says:

      @David – I was (and am) not aware that this violates any license agreement. I am an engineer and not a lawyer. If anyone thinks that it violates the license then they should indeed not use this technique. I do NOT condone any action that violates the license.

      I do not see, in my non-expert eyes, how this could violate any Matlab license, considering the fact that we’re not modifying the Matlab figure’s frame/border – we’re simply creating a new Java JFrame, which coexists alongside the original (decorated) Matlab figure. Doing this should be perfectly ok, I think. Note that this is not legal advise in any way – I am NOT a lawyer. Again – if you think that doing this is problematic, then you should not do it.

  2. Ruben Faibish says:

    Hi Yair

    The line:

    jFrame = javaObjectEDT(javax.swing.JFrame(figTitle));

    gives me an Undefined variable "javax" or class "javax.swing.jFrame". message

    By the way, about the violation of Matlab licence, I think that if the figure still appears in the taskbar with a Matlab icon, then you are not hiding the Matlab belonging of the figure.
    But I am a physicist and not a lawyer so don’t use it as a legal advice.

    • Yair Altman says:

      @Ruben – you got the error because you spelled JFrame (in javax.swing.JFrame) with a lowercase j instead of an uppercase J

  3. Cheung says:

    Now I meet a ticklish problem when I use your tool,’undecorateFig’. First I would like to describe my purpose. When we run a GUI we write, a corresponding figure always appear. now I want to full screen the figure, not just maximizing the figure, simultaneously, the most important is that the titlebar of the figure and taskbar should all be hidden or removed. The ‘undecorateFig’ tool can remove titlebar and taskbar of the figure, but the figure is not a full-screen figure, the area corresponding to the previous titlebar isn’t filled by the background of figure. I don’t know how to deal with it. Could you help me? Thanks. For example, I have the following codes:

    % the function can display two images. when the 'OK' button is pressed, the second image would appear.  
    function display_img 
       screen = get(0,'ScreenSize');
       H1=figure('menubar','none');
       set(H1,'Position',[0, 0, screen(3),screen(4)]);
       img1 = imread('peppers.png'); 
       subplot(1,2,1);
       subimage(img1);
       axis off;
       H2 = uicontrol('Style','pushbutton','String','OK','Position',[200,100,100,40],'Fontsize',12);
       set(H2,'callback','my_callbacks');
       jFrame = undecorateFig(H1);
     
    function my_callbacks
       img2 = imread('peppers.png'); 
       subplot(1,2,2);
       subimage(img2);
       axis off;

    However, after I use ‘undecorateFig’ tool, the ‘OK’ button doesn’t work .

    Best regards!

    • @Cheung – you can increase the figure’s height by 20 pixels to take into account the titlebar (i.e., screen(4)+20).

      As for your OK button, you should review how to specify Matlab callbacks.

    • Cheung says:

      @Yair – Thank you for your speedy response. I have tired your suggestion, but it doesn’t work,and remains the previous status, 20 pixels isn’t increased, I don’t why.

  4. Bryan Moosman says:

    Hello Yair,

    I’ve tried both the code above and the functions you submitted. I’ve got 2 areas of feedback:

    1. The figure sizing isn’t quite correct. It was a bit large and I had to remove the “+mjr.getY” term to get it to fit properly.
    2. There are focus traversal problems with the current function. When I use it, I’m unable to input any text or use any of the UI elements inside as the focus keeps transferring elsewhere before any text can be input.

    On a side note, I’m using this to generate a popup menu inside of another GUI. It works pretty well, and I don’t think it would be too much work to clean it up. I think it would do well as a MATLAB file submission if labeled as a ‘popup’ menu template.

    By the way, I’m using 2014a.

  5. Iliya says:

    Hello Yair,

    I started seeing the following warning when trying to access the JavaFrame property:

    Warning: figure JavaFrame property will be obsoleted in a future release. For more information see the JavaFrame resource on the MathWorks web site.

    I might be mistaken here, but I don’t think I saw this before R2015a. Should we be worried?

    I mainly use it to maximize figures, as per your tip from long ago, and did not look into new methods since the introduction of HG2. Is there another way to maximize figures now without getting warnings?

    Thanks!

  6. David M says:

    Hello Yair,

    Is it possible to grab and drag a ‘frameless’ figure without the title bar? I would like to click somewhere in the frameless figure area, and drag it around.

    Kind regards

    • @David – you will need to do this programmatically: this is typically handled by the window frame but without the frame it doesn’t happen automatically. So trap a mouse click event and then move the figure as per subsequent mouse-movement events.

Leave a Reply


Your email address will not be published. Required fields are marked *