- Undocumented Matlab - https://undocumentedmatlab.com -

Scrollable GUI panels

Posted By Yair Altman On July 25, 2018 | 3 Comments

Matlab enables two types of GUI container types, via the Units property: fixed-size ('pixels', 'chars', etc.) and flexible ('normalized'). In many cases, we need something in between: a panel that expands dynamically when its container grows (i.e., flexible/normalized), and displays scroll-bars when the container shrinks (i.e., fixed size, with a scrollable viewport). This functionality is relatively easy to achieve using a bit of undocumented magic powder. Today’s post will show how to do this with legacy (Java-based) figures, and next week’s post will do the same for web-based (JavaScript) uifigures.

Scrollable Matlab GUI panel
Scrollable Matlab GUI panel

Technical description

The basic idea is that in HG2 (Matlab release R2014b onward), uipanels are implemented using standard Java JPanel components. This enables all sorts of interesting customizations [1]. For the purposes of today’s discussion, the important thing to note is that the underlying JPanel object can be re-parented to reside inside a Java JScrollPanel [2].
So, the idea is to get the Matlab panel’s underlying JPanel object reference, then embed it within a new JScrollPanel object that is placed at the exact same GUI coordinates as the original panel. The essential Matlab code snippet is this:

% Create the Matlab uipanel in the GUI
hPanel = uipanel(...); drawnow
% Get the panel's underlying JPanel object reference
jPanel = hPanel.JavaFrame.getGUIDEView.getParent;
% Embed the JPanel within a new JScrollPanel object
jScrollPanel = javaObjectEDT(javax.swing.JScrollPane(jPanel));
% Remove the JScrollPane border-line
% Place the JScrollPanel in same GUI location as the original panel
pixelpos = getpixelposition(hPanel);
hParent = hPanel.Parent;
[hjScrollPanel, hScrollPanel] = javacomponent(jScrollPanel, pixelpos, hParent);
hScrollPanel.Units = 'norm';
% Ensure that the scroll-panel and contained panel have linked visibility
hLink = linkprop([hPanel,hScrollPanel],'Visible');

Note that this code will only work with panels created in legacy figures, not web-based uifigures (as I mentioned above, a similar solution for uifigures will be presented here next week).
Also note that the new scroll-panel is created with javaObjectEDT, in order to avoid EDT synchronization problems [3]
We also want to link the visibility of the scroll-panel and its contained Matlab panel (hPanel), so that when the panel is set to be non-visible (hPanel.Visible='off'), the entire scroll-panel (scrollbars included) will become invisible, and vice-versa. We can do this by linking the Visible property of the Matlab panel and the scroll-panel container (hScrollPanel) using the linkprop function at the bottom of the script above. Note that we must persist the resulting hLink otherwise it becomes defunct – this is done by using setappdata to store the link in the panel (this way, when the panel is deleted, so does the link).

Resizing the container

The scroll-panel is created with a specific pixelpos location and size, and then its container is made to have normalized units. This ensures that when the container (hParent) grows, the scroll-panel grows as well, and no scrollbars appear (since they are not needed). But when the container shrinks in the X and/or Y direction, corresponding scrollbars appear as-needed. It sounds complicated, but it’s actually very intuitive, as the animated image above shows.
When the container resizes, the displayed viewport image may “jump” sideways. To fix this we can attach a simple repaint callback function to the scroll-panel’s SizeChangedFcn property:

% Attach a repaint callback function
hScrollPanel.SizeChangedFcn = @repaintScrollPane;
% Define the callback function:
function repaintScrollPane(hScrollPanel, varargin)
    jScrollPanel = hScrollPanel.JavaPeer;
    offsetX = 0; %or: jScrollPanel.getHorizontalScrollBar.getValue;
    offsetY = 0; %or: jScrollPanel.getVerticalScrollBar.getValue;
    jOffsetPoint = java.awt.Point(offsetX, offsetY);
    jViewport = jScrollPanel.getViewport;

Scrollbars automatically appear as-needed during resize
Scrollbars automatically appear as-needed during resize

Viewport position/offset

It would be convenient to have an easy-to-use ViewOffset property in the hScrollPanel object, in order to be able to programmatically query and set the current viewport position (i.e., scrollbars offset). We can add this property via the addprop function:

% Add a new Viewoffset property to hSCrollPanel object
hProp = addprop(hScrollPanel, 'ViewOffset');
hProp.GetMethod = @getViewOffset;  %viewOffset = getViewOffset(hScrollPanel)
hProp.SetMethod = @setViewOffset;  %setViewOffset(hScrollPanel, viewOffset)
% Getter method for the dynamic ViewOffset property
function viewOffset = getViewOffset(hScrollPanel, varargin)
    jScrollPanel = hScrollPanel.JavaPeer;
    jPoint = jScrollPanel.getViewport.getViewPosition;
    viewOffset = [jPoint.getX, jPoint.getY];
% Setter method for the dynamic ViewOffset property
function setViewOffset(hScrollPanel, viewOffset)
    jPoint = java.awt.Point(viewOffset(1), viewOffset(2));
    jScrollPanel = hScrollPanel.JavaPeer;

This enables us to both query and update the scroll-panel’s view position – [0,0] means top-left corner (i.e., no scroll); [12,34] mean scrolling 12 to the right and 34 down:

>> offset = hScrollPanel.ViewOffset   % or: get(hScrollPanel,'ViewOffset')
offset =
     0     0
>> offset = hScrollPanel.ViewOffset   % or: get(hScrollPanel,'ViewOffset')
offset =
    12    34
% Scroll 30 pixels right, 50 pixels down
>> hScrollPanel.ViewOffset = [30,50];   % or: set(hScrollPanel,'ViewOffset',[30,50])

attachScrollPanelTo utility

I have prepared a utility called attachScrollPanelTo (downloadable from the Matlab File Exchange [4]), which encapsulates all of the above, plus a few other features: inputs validation, Viewport property in the output scroll-pane object, automatic encasing in a new panel for input object that are not already a panel, etc. Feel free to download the utility, use it in your program, and modify the source-code to fit your needs. Here are some usage examples:

attachScrollPanelTo();  % display the demo
attachScrollPanelTo(hPanel) % place the specified hPanel in a scroll-panel
hScroll = attachScrollPanelTo(hPanel);
hScroll.ViewOffset = [30,50];  % set viewport offset (30px right, 50px down)
set(hScroll, 'ViewOffset',[30,50]);  % equivalent alternative

If you’d like me to add flare to your Matlab GUI, don’t hesitate to contact me on my Consulting page [5].

Categories: GUI, Java, Medium risk of breaking in future versions, Undocumented feature

3 Comments (Open | Close)

3 Comments To "Scrollable GUI panels"

#1 Comment By David Sampson On August 4, 2018 @ 11:24

See also uix.ScrollingPanel in [12]. This works for figures (not uifigures) from R2014b onwards.

#2 Comment By Eivind On October 7, 2020 @ 21:38


Thanks for a great function! I was trying to put the main panel of my figure into a scrollable panel with the goal of zooming into the panel using shift+scroll and scrolling up and down using normal scroll. I realised quickly however, that the zoom function I added to the WindowScrollWheelFcn property of my figure does not execute on scrolling because the scrolling is captured by the scroll panel.

I’m not familiar enough with java to find out how to capture the scroll events and “re-route” them to my figure if the shift button is down, but I thought of an easier solution, i.e to detach the main panel from the scroll panel whenever I press the shift button and reattach it when I release the shift button. However, I got stuck here too, as I have no idea what to reparent the “jParent” to in what would be the reverse of this:

jPanel = hPanel.JavaFrame.getGUIDEView;
jParent = jPanel.getParent;
jScrollPanel = javaObjectEDT(javax.swing.JScrollPane(jParent));

I guess what I am trying to write is a detachScrollPanel function (similar to the decorateFig/undecorateFig functions). Would appreciate any help!

#3 Comment By Yair Altman On October 8, 2020 @ 12:28

jPanel1.setParent(jPanel2) will reparent jPanel1 (and similarly any Java component/container) inside jPanel2 (any Java container).
While this might work, it seems to me to be an overly-complex solution. A much simpler one is to set your jScrollPanel’s MouseWheelMovedCallback callback as explained here: [13]

Article printed from Undocumented Matlab: https://undocumentedmatlab.com

URL to article: https://undocumentedmatlab.com/articles/scrollable-gui-panels

URLs in this post:

[1] interesting customizations: https://undocumentedmatlab.com/blog/customizing-matlab-uipanels

[2] JScrollPanel: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html

[3] EDT synchronization problems: https://undocumentedmatlab.com/blog/matlab-and-the-event-dispatch-thread-edt

[4] downloadable from the Matlab File Exchange: https://www.mathworks.com/matlabcentral/fileexchange/68325-attachscrollpanelto-add-scroll-panel-to-a-uipanel-or-axes

[5] Consulting page: https://undocumentedmatlab.com/consulting

[6] Tab panels – uitab and relatives : https://undocumentedmatlab.com/articles/tab-panels-uitab-and-relatives

[7] Syntax highlighted labels & panels : https://undocumentedmatlab.com/articles/syntax-highlighted-labels-panels

[8] Customizing Matlab uipanels : https://undocumentedmatlab.com/articles/customizing-matlab-uipanels

[9] Transparent uipanels : https://undocumentedmatlab.com/articles/transparent-uipanels

[10] Builtin PopupPanel widget : https://undocumentedmatlab.com/articles/builtin-popuppanel-widget

[11] Editbox data input validation : https://undocumentedmatlab.com/articles/editbox-data-input-validation

[12] : https://www.mathworks.com/matlabcentral/fileexchange/47982-gui-layout-toolbox

[13] : https://undocumentedmatlab.com/articles/uicontrol-callbacks

Copyright © Yair Altman - Undocumented Matlab. All rights reserved.