I have an application where an SVG image is presented to the user, and they need to fill in two edit fields based on what is presented. Since the process needs to be repeated many times, I concluded it would be best for speed and efficiency if user interactions required the keyboard alone. Towards that end, I must ensure several things:
The 1st requirement can be fulfilled by toggling figure visibility, as explained here.
The 2nd requirement is also fairly simple to fulfill, and merely requires that graphical elements are defined in a specific order, as discussed here (for uifigures) and here (for figures).
My difficulty is with the 3rd requirement, and specifically - I have no idea how to ensure the desired edit field is focused when needed. Please consider the following class for reference, in which the focusControl
method is just a placeholder.
classdef SVGAxisLimit < handle
properties (GetAccess = private, SetAccess = immutable)
hF (1,1)
hI (1,1) matlab.ui.control.Image
hLL (1,1) matlab.ui.control.NumericEditField
hRL (1,1) matlab.ui.control.NumericEditField
hDone (1,1) matlab.ui.control.Button
end
methods
function obj = SVGAxisLimit()
% Create figure:
hF = uifigure('WindowState','maximized','Color','w'); drawnow;
% Create image:
hI = uiimage(hF, 'Position', [1,100,hF.Position(3),hF.Position(4)-100]);
% Create controls:
uilabel(hF, 'HorizontalAlignment', 'left', 'Position', [600 20 150 42],...
'Text', 'Left Limit:', 'FontSize', 22);
% Create LeftLimitEditField
hLL = uieditfield(hF, 'numeric', 'Position', [710 20 80 42], 'FontSize', 22);
% Create RightLimitEditFieldLabel
uilabel(hF, 'HorizontalAlignment', 'left', 'Position', [900 20 150 42],...
'Text', 'Right Limit:', 'FontSize', 22);
% Create RightLimitEditField
hRL = uieditfield(hF, 'numeric', 'Position', [1025 20 80 42], 'FontSize', 22);
% Create DoneButton
hDone = uibutton(hF, 'push', 'Text', 'Done', 'Position', [1200 20 80 42], ...
'FontWeight', 'bold', 'FontSize', 22, 'ButtonPushedFcn', @(varargin)uiresume(hF));
% Store handles:
obj.hF = hF;
obj.hI = hI;
obj.hLL = hLL;
obj.hRL = hRL;
obj.hDone = hDone;
end
end
methods (Access = public)
function [realLims] = showSVG(salObj, svgPath)
salObj.hI.ImageSource = svgPath;
% Focus left edit field
SVGAxisLimit.focusControl(salObj.hLL);
% Wait for a click on "done"
uiwait(salObj.hF);
% When resume, capture values:
realLims = [salObj.hLL.Value, salObj.hRL.Value];
end
end
methods (Access = private, Static = true)
function [] = focusControl(hObject)
% hObject is the handle of the uicontrol which needs to be focused
% ???
end
end
end
I am using MATLAB R2020a.
P.S.I have decided to use uifigures for this, because their uiimage
component natively supports the presentation of SVGs (although workarounds avoiding this component exist).
Seeing how a UIFigure
is mostly a webpage, it turns out that the JavaScript Web API methods of .focus()
and .select()
can be useful here. The only difficulty that remains is finding some way to refer to the web element (widget) in question. Fortunately, the HTML elements corresponding to edit fields in R2020a are identified by a unique id
attribute, which makes it easy to refer to them using very simple DOM commands such as getElementById
. In summary:
function [] = focusControl(hObject)
% hObject is the handle of the uicontrol which needs to be focused
[hWin, widgetID] = mlapptools.getWebElements(hObject);
hWin.executeJS(sprintf(...
'W = document.getElementById("%s"); W.focus(); W.select();', widgetID.ID_val));
end
Where mlapptools
can be found here (disclosure: I am a co-author of this utility).