From 4761225e4474dde35961c1c251e993118e0c42d9 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 2 Jan 2018 21:05:43 +0200 Subject: [PATCH 01/14] Fixing a bug with decodeDijitRegistryResult --- mlapptools.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 33c7cac..6266b63 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -303,9 +303,9 @@ function textAlign(uiElement, alignment) props = jsondecode(win.executeJS(sprintf('Object.keys(W[%d])', ind1-1))); tmp = mlapptools.emptyStructWithFields(props); validProps = fieldnames(tmp); - for indP = 1:numel(tmp) + for indP = 1:numel(validProps) try - tmp.(validProps(indP)) = jsondecode(win.executeJS(sprintf(['W[%d].' props{ind1}], ind1-1))); + tmp.(validProps{indP}) = jsondecode(win.executeJS(sprintf(['W[%d].' props{indP}], ind1-1))); catch % Fallback could be executed recursively for all problematic field % (to keep the most data), but for now do nothing. From 3267a8fb74a28a70f7871fa1689cebd941604edb Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Wed, 3 Jan 2018 13:53:22 +0200 Subject: [PATCH 02/14] Relocated "figFromWebwindow" and explained what it does. --- mlapptools.m | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 6266b63..9e82e8a 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -333,7 +333,20 @@ function textAlign(uiElement, alignment) data_tag = char(struct(uiElement).Controller.ProxyView.PeerNode.getId); warning(warnState); end % getDataTag - + + function hFig = figFromWebwindow(hWebwindow) + % Using this method is discouraged as it's relatively computation-intensive. + % Since the figure handle is not a property of the webwindow or its children + % (to our best knowledge), we must list all figures and check which of them + % is associated with the input webwindow. + hFigs = findall(groot, 'Type', 'figure'); + warnState = mlapptools.toggleWarnings('off'); + hUIFigs = hFigs(arrayfun(@(x)isstruct(struct(x).ControllerInfo), hFigs)); + ww = arrayfun(@mlapptools.getWebWindow, hUIFigs); + warning(warnState); % Restore warning state + hFig = hFigs(hWebwindow == ww); + end % figFromWebwindow + function [widgetID] = getWidgetID(win, data_tag) widgetquerystr = sprintf('dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag); @@ -447,15 +460,6 @@ function validateAlignmentStr(alignment) end end % validateFontWeight - function hFig = figFromWebwindow(hWebwindow) - % Using this method is discouraged. - hFigs = findall(groot, 'Type', 'figure'); - warnState = mlapptools.toggleWarnings('off'); - hUIFigs = hFigs(arrayfun(@(x)isstruct(struct(x).ControllerInfo), hFigs)); - ww = arrayfun(@mlapptools.getWebWindow, hUIFigs); - warning(warnState); % Restore warning state - hFig = hFigs(hWebwindow == ww); - end % figFromWebwindow end % Private Static Methods From 289d608e008b1809c998878a9a2ea7e71d285b39 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Wed, 3 Jan 2018 13:57:31 +0200 Subject: [PATCH 03/14] Replaced hard-coded QUERY_TIMEOUT string with a constant. --- mlapptools.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 9e82e8a..14968b7 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -22,6 +22,7 @@ properties (Access = private, Constant = true) QUERY_TIMEOUT = 5; % Dojo query timeout period, seconds + TAG_TIMEOUT = 'QUERY_TIMEOUT'; end methods (Access = public, Static = true) @@ -252,7 +253,7 @@ function fontWeight(uiElement, weight) function setTimeout(hUIFig, newTimeoutInSec) % Sets a custom timeout for dojo queries, specified in [s]. - setappdata(hUIFig, 'QUERY_TIMEOUT', newTimeoutInSec); + setappdata(hUIFig, mlapptools.TAG_TIMEOUT, newTimeoutInSec); end function textAlign(uiElement, alignment) @@ -376,7 +377,7 @@ function textAlign(uiElement, alignment) end % getWidgetID function to = getTimeout(hFig) - to = getappdata(hFig,'QUERY_TIMEOUT'); + to = getappdata(hFig, mlapptools.TAG_TIMEOUT); if isempty(to), to = mlapptools.QUERY_TIMEOUT; end end % getTimeout From 697f6c9d5fe74d73667440e284d92520dadd397a Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Wed, 3 Jan 2018 14:10:09 +0200 Subject: [PATCH 04/14] Better testing for "Did figure finish loading?" - Added the private methods `waitTillFigureLoaded` and `waitTillWebwindowLoaded`. - Added the public method `waitForFigureReady(hUIFig)` - this blocking method returns only after determining that the UIFigure was fully loaded, or if 2*timeout was reached. - Refactored a couple of functions (getWebWindow, getWidgetID) to make their inner workings much clearer. - TODO: add the new public method to Readme.md. - TODO: test on older MATLAB versions. --- mlapptools.m | 136 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 55 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 14968b7..3ef443f 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -16,6 +16,7 @@ % setStyle - Modify a specified style property. % setTimeout - Override the default timeout for dojo commands, for a specific uifigure. % textAlign - Modify text alignment. + % waitForFigureReady - A blocking method that only returns after the uifigure is fully loaded. % % See README.md for detailed documentation and examples. @@ -107,41 +108,20 @@ function fontWeight(uiElement, weight) function [win] = getWebWindow(hUIFig) warnState = mlapptools.toggleWarnings('off'); - % Make sure we got a valid handle - assert(mlapptools.isUIFigure(hUIFig),... - 'mlapptools:getWebWindow:NotUIFigure',... - 'The provided window handle is not of a UIFigure.'); - to = mlapptools.getTimeout(hUIFig); - tic - while true && (toc < to) - try - hController = struct(struct(hUIFig).Controller); - % Check for Controller version: - switch subsref(ver('matlab'), substruct('.','Version')) - case {'9.0','9.1'} % R2016a or R2016b - win = hController.Container.CEF; - otherwise % R2017a onward - win = struct(hController.PlatformHost).CEF; - end - break - catch err - if strcmp(err.identifier, 'MATLAB:nonExistentField') - pause(0.01) - else - warning(warnState); % Restore warning state - rethrow(err) - end - end - end - warning(warnState); % Restore warning state + mlapptools.waitTillFigureLoaded(hUIFig); + % Since the above checks if a Controller exists, the below should work. - if toc >= to - msgID = 'mlapptools:getWidgetID:QueryTimeout'; - error(msgID, ... - 'WidgetID query timed out after %u seconds, UI needs more time to load', ... - to); + hController = struct(struct(hUIFig).Controller); + % Check for Controller version: + switch subsref(ver('matlab'), substruct('.','Version')) + case {'9.0','9.1'} % R2016a or R2016b + win = hController.Container.CEF; + otherwise % R2017a onward + win = struct(hController.PlatformHost).CEF; end + + warning(warnState); % Restore warning state end % getWebWindow @@ -267,6 +247,23 @@ function textAlign(uiElement, alignment) win.executeJS(alignSetStr); end % textAlign + function win = waitForFigureReady(hUIFig) + % This blocking method waits until a UIFigure and its widgets have fully loaded. + %% Make sure that the handle is valid: + assert(mlapptools.isUIFigure(hUIFig),... + 'mlapptools:getWebWindow:NotUIFigure',... + 'The provided window handle is not of a UIFigure.'); + assert(strcmp(hUIFig.Visible,'on'),... + 'mlapptools:getWebWindow:FigureNotVisible',... + 'Invisible figures are not supported.'); + %% Wait for the figure to appear: + mlapptools.waitTillFigureLoaded(hUIFig); + %% Make sure that Dojo is ready: + % Get a handle to the webwindow + win = mlapptools.getWebWindow(hUIFig); + mlapptools.waitTillWebwindowLoaded(win, hUIFig); + end % waitForFigureReady + end % Public Static Methods methods (Static = true, Access = private) @@ -350,29 +347,14 @@ function textAlign(uiElement, alignment) function [widgetID] = getWidgetID(win, data_tag) widgetquerystr = sprintf('dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag); - - to = mlapptools.getTimeout(mlapptools.figFromWebwindow(win)); - tic - while true && (toc < to) - try - widgetID = win.executeJS(widgetquerystr); - widgetID = widgetID(2:end-1); - break - catch err - if ~isempty(strfind(err.message, 'JavaScript error: Uncaught ReferenceError: dojo is not defined')) || ... - ~isempty(strfind(err.message, 'Cannot read property ''widgetid'' of null')) - pause(0.01) - else - rethrow(err) - end - end - end - - if toc >= to - msgID = 'mlapptools:getWidgetID:QueryTimeout'; - error(msgID, ... - 'widgetID query timed out after %u seconds, UI needs more time to load', ... - to); + hFig = mlapptools.figFromWebwindow(win); + mlapptools.waitTillFigureLoaded(win, hFig); + try % should work for most UI objects + widgetID = win.executeJS(widgetquerystr); + widgetID = widgetID(2:end-1); + catch % fallback for problematic objects + warning('Problematic control encountered with no fallback implemented yet. Please ') + % TODO end end % getWidgetID @@ -460,7 +442,51 @@ function validateAlignmentStr(alignment) error(msgID, 'Invalid font weight specified: ''%s''', weight); end end % validateFontWeight + + function waitTillFigureLoaded(hFig) + % A blocking method that ensures a UIFigure has fully loaded. + warnState = mlapptools.toggleWarnings('off'); + to = mlapptools.getTimeout(hFig); + tic + while (toc < to) && isempty(struct(hFig).Controller) + pause(0.01) + end + if toc > to + msgID = 'mlapptools:waitTillFigureLoaded:TimeoutReached'; + error(msgID, ... + ['Waiting for the figure to load has timed out after %u seconds. ' ... + 'Try increasing the timeout. If the figure clearly loaded in time, yet '... + 'this error remains - it might be a bug in the tool! ' ... + 'Please let the developers know through GitHub.'], ... + to); + end + warning(warnState); + end % waitTillFigureLoaded + function waitTillWebwindowLoaded(hWebwindow, hFig) + % A blocking method that ensures a certain webwindow has fully loaded. + if nargin < 2 + hFig = mlapptools.figFromWebwindow(hWebwindow); + end + + to = mlapptools.getTimeout(hFig); + tic + while (toc < to) && ~jsondecode(hWebwindow.executeJS(... + 'this.hasOwnProperty("require") && require !== undefined && typeof(require) === "function"')) + pause(0.01) + end + if toc > to + msgID = 'mlapptools:waitTillWebwindowLoaded:TimeoutReached'; + error(msgID, ... + ['Waiting for the webwindow to load has timed out after %u seconds. ' ... + 'Try increasing the timeout. If the figure clearly loaded in time, yet '... + 'this error remains - it might be a bug in the tool! ' ... + 'Please let the developers know through GitHub.'], ... + to); + else + hWebwindow.executeJS('require(["dojo/ready"], function(ready){});'); + end + end % waitTillWebwindowLoaded end % Private Static Methods From 2bd87fc4012ab5858812e31b4c18aa9e257bcf3d Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Wed, 3 Jan 2018 14:11:16 +0200 Subject: [PATCH 05/14] Small adaptation for getWebWindow getting the ancestor automatically --- mlapptools.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlapptools.m b/mlapptools.m index 3ef443f..b76b951 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -100,7 +100,7 @@ function fontWeight(uiElement, weight) % A method for obtaining the webwindow handle and the widgetID corresponding % to the provided uifigure control. % Get a handle to the webwindow - win = mlapptools.getWebWindow(uiElement.Parent); + win = mlapptools.getWebWindow(uiElement); % Find which element of the DOM we want to edit widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); From 46cec70a09711b00c38dedf75bfd07948f186a0e Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Wed, 3 Jan 2018 15:47:09 +0200 Subject: [PATCH 06/14] General fixes - Ignoring hidden figures (that don't have Controllers) when trying to convert hWebwindow -> hUIFigure. - Corrected call to waitTillFigureLoaded. - Improved unfinished warning message. --- mlapptools.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index b76b951..ae13cc0 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -340,6 +340,7 @@ function textAlign(uiElement, alignment) hFigs = findall(groot, 'Type', 'figure'); warnState = mlapptools.toggleWarnings('off'); hUIFigs = hFigs(arrayfun(@(x)isstruct(struct(x).ControllerInfo), hFigs)); + hUIFigs = hUIFigs(strcmp({hUIFigs.Visible},'on')); % Hidden figures are ignored ww = arrayfun(@mlapptools.getWebWindow, hUIFigs); warning(warnState); % Restore warning state hFig = hFigs(hWebwindow == ww); @@ -348,12 +349,12 @@ function textAlign(uiElement, alignment) function [widgetID] = getWidgetID(win, data_tag) widgetquerystr = sprintf('dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag); hFig = mlapptools.figFromWebwindow(win); - mlapptools.waitTillFigureLoaded(win, hFig); + mlapptools.waitTillFigureLoaded(hFig); try % should work for most UI objects widgetID = win.executeJS(widgetquerystr); widgetID = widgetID(2:end-1); catch % fallback for problematic objects - warning('Problematic control encountered with no fallback implemented yet. Please ') + warning('Problematic control encountered with no fallback implemented yet.') % TODO end end % getWidgetID From 345376fef8e98a6d6f908e91c5fc76c52da67b1a Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Wed, 3 Jan 2018 20:01:21 +0200 Subject: [PATCH 07/14] Improved method for UI object identification. - widgetID is now a structure (this is more robust). - Refactored helper styling methods - now all go through setStyle. - Adapted TableDemo to new ID conventions. - getWidgetInfo is possibly broken (due to the "registry.byId" call which doesn't work in the general ID_struct case). Internal documentation updated accordingly. --- Demo/TableDemo.m | 24 ++++++++----- mlapptools.m | 88 +++++++++++++++++++++++++++++++----------------- 2 files changed, 73 insertions(+), 39 deletions(-) diff --git a/Demo/TableDemo.m b/Demo/TableDemo.m index de4da8b..5d25503 100644 --- a/Demo/TableDemo.m +++ b/Demo/TableDemo.m @@ -6,12 +6,18 @@ UITable matlab.ui.control.Table Button matlab.ui.control.Button end + + properties (Access = private, Constant = true) + ID_ATTRIBUTE_NAME = 'id'; + end methods (Access = private) % Button pushed function: Button function ButtonPushed(app, ~) - % Return all registered widgets: + IAN = app.ID_ATTRIBUTE_NAME; + hWin = mlapptools.getWebWindow(app.UIFigure); + % Return all registered widgets: [~,w] = mlapptools.getWidgetList(app.UIFigure); % Filter list: w = w(~cellfun(@isempty,w.id) & ... @@ -19,35 +25,35 @@ function ButtonPushed(app, ~) % Apply random styles: for ind1 = 1:4:size(w,1) mlapptools.setStyle(... - app.UIFigure,... + hWin,... 'border',... '2px solid red',... - w{ind1,'id'}{1}); + struct('ID_attr',IAN,'ID_val',w{ind1,IAN}{1})); end for ind2 = 2:4:size(w,1) mlapptools.setStyle(... - app.UIFigure,... + hWin,... 'background-image',... 'url(http://lorempixel.com/40/120/)',... - w{ind2,'id'}{1}); + struct('ID_attr',IAN,'ID_val',w{ind2,IAN}{1})); end for ind3 = 3:4:size(w,1) mlapptools.setStyle(... - app.UIFigure,... + hWin,... 'background-color',... ['rgb(' num2str(randi(255)) ',' num2str(randi(255)) ',' ... num2str(randi(255)) +')'],... - w{ind3,'id'}{1}); + struct('ID_attr',IAN,'ID_val',w{ind3,IAN}{1})); end for ind4 = 4:4:size(w,1) mlapptools.setStyle(... - app.UIFigure,... + hWin,... 'padding',... '0cm 1cm 0cm 0cm',... - w{ind4,'id'}{1}); + struct('ID_attr',IAN,'ID_val',w{ind4,IAN}{1})); end end diff --git a/mlapptools.m b/mlapptools.m index ae13cc0..9fcbf0b 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -24,6 +24,7 @@ properties (Access = private, Constant = true) QUERY_TIMEOUT = 5; % Dojo query timeout period, seconds TAG_TIMEOUT = 'QUERY_TIMEOUT'; + DEF_ID_ATTRIBUTE = 'id'; end methods (Access = public, Static = true) @@ -53,21 +54,19 @@ function fontColor(uiElement, newcolor) % A method for manipulating text color. newcolor = mlapptools.validateCSScolor(newcolor); - [win, widgetID] = mlapptools.getWebElements(uiElement); + [win, ID_struct] = mlapptools.getWebElements(uiElement); - fontColorSetStr = sprintf('dojo.style(dojo.query("#%s")[0], "color", "%s")', widgetID, newcolor); - win.executeJS(fontColorSetStr); + mlapptools.setStyle(win, 'color', newcolor, ID_struct); end % fontColor function fontWeight(uiElement, weight) % A method for manipulating font weight, which controls how thick or % thin characters in text should be displayed. - weight = mlapptools.validateFontWeight(weight); + weight = mlapptools.validateFontWeight(weight); - [win, widgetID] = mlapptools.getWebElements(uiElement); + [win, ID_struct] = mlapptools.getWebElements(uiElement); - fontWeightSetStr = sprintf('dojo.style(dojo.query("#%s")[0], "font-weight", "%s")', widgetID, weight); - win.executeJS(fontWeightSetStr); + mlapptools.setStyle(win, 'font-weight', weight, ID_struct); end % fontWeight function [fullHTML] = getHTML(hUIFig) @@ -126,7 +125,8 @@ function fontWeight(uiElement, weight) end % getWebWindow function [nfo] = getWidgetInfo(win, widgetID, verboseFlag) - % A method for gathering information about a specific dijit widget. + % A method for gathering information about a specific dijit widget, if its + % HTML div id is known. %% Handling required positional inputs: assert(nargin >= 2,'mlapptools:getWidgetInfo:insufficientInputs',... 'getWidgetInfo must be called with at least 2 inputs.'); @@ -136,7 +136,7 @@ function fontWeight(uiElement, weight) end %% Querying dijit win.executeJS(['var W; require(["dijit/registry"], '... - 'function(registry){W = registry.byId("' widgetID '");}); W = [W];']); + 'function(registry){W = registry.byId("' widgetID.ID_val '");}); W = [W];']); % Decoding try nfo = mlapptools.decodeDijitRegistryResult(win,verboseFlag); @@ -194,8 +194,9 @@ function fontWeight(uiElement, weight) % 3-parameter call: % widgetID = setStyle(hControl, styleAttr, styleValue) % 4-parameter call: - % setStyle(hUIFig, styleAttr, styleValue, widgetID) + % setStyle(hWin, styleAttr, styleValue, ID_struct) + narginchk(3,4); % Unpack inputs: styleAttr = varargin{2}; styleValue = varargin{3}; @@ -204,16 +205,15 @@ function fontWeight(uiElement, weight) case 3 hControl = varargin{1}; % Get a handle to the webwindow - [win, widgetID] = mlapptools.getWebElements(hControl); + [win, ID_struct] = mlapptools.getWebElements(hControl); case 4 - hUIFig = varargin{1}; - widgetID = varargin{4}; - - % Get a handle to the webwindow - win = mlapptools.getWebWindow(hUIFig); - end - - styleSetStr = sprintf('dojo.style(dojo.query("#%s")[0], "%s", "%s")', widgetID, styleAttr, styleValue); + % If we know the ID_struct, the webwindow handle must be available + win = varargin{1}; + ID_struct = varargin{4}; + end + + styleSetStr = sprintf('dojo.style(dojo.query("[%s = ''%s'']")[0], "%s", "%s")',... + ID_struct.ID_attr, ID_struct.ID_val, styleAttr, styleValue); % ^ this might result in junk if widgetId=='null'. try win.executeJS(styleSetStr); @@ -226,7 +226,7 @@ function fontWeight(uiElement, weight) % Assign outputs: if nargout >= 1 - varargout{1} = widgetID; + varargout{1} = ID_struct; end end % setStyle @@ -241,10 +241,9 @@ function textAlign(uiElement, alignment) alignment = lower(alignment); mlapptools.validateAlignmentStr(alignment) - [win, widgetID] = mlapptools.getWebElements(uiElement); + [win, ID_struct] = mlapptools.getWebElements(uiElement); - alignSetStr = sprintf('dojo.style(dojo.query("#%s")[0], "textAlign", "%s")', widgetID, alignment); - win.executeJS(alignSetStr); + mlapptools.setStyle(win, 'textAlign', alignment, ID_struct); end % textAlign function win = waitForFigureReady(hUIFig) @@ -346,17 +345,46 @@ function textAlign(uiElement, alignment) hFig = hFigs(hWebwindow == ww); end % figFromWebwindow - function [widgetID] = getWidgetID(win, data_tag) + function [ID_struct] = getWidgetID(win, data_tag) + % This method returns a structure containing some uniquely-identifying information + % about a DOM node. widgetquerystr = sprintf('dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag); - hFig = mlapptools.figFromWebwindow(win); - mlapptools.waitTillFigureLoaded(hFig); + mlapptools.waitTillWebwindowLoaded(win); try % should work for most UI objects - widgetID = win.executeJS(widgetquerystr); - widgetID = widgetID(2:end-1); + ID = win.executeJS(widgetquerystr); + ID_struct = struct('ID_attr', mlapptools.DEF_ID_ATTRIBUTE, 'ID_val', ID(2:end-1)); catch % fallback for problematic objects - warning('Problematic control encountered with no fallback implemented yet.') - % TODO + % We retry by using the dijit registry: + win.executeJS(['var W; require(["dijit/registry"], '... + 'function(registry){W = registry.toArray().map(x => x.domNode.childNodes);});']); + nWidgets = jsondecode(win.executeJS('W.length')); + try + for ind1 = 0:nWidgets-1 + nChild = jsondecode(win.executeJS(sprintf('W[%d].length',ind1))); + for ind2 = 0:nChild-1 + tmp = win.executeJS(sprintf('W[%d][%d].dataset',ind1,ind2)); + if isempty(tmp) + continue + else + tmp = jsondecode(tmp); + end + if isfield(tmp,'tag') && strcmp(tmp.tag,data_tag) + ID = win.executeJS(sprintf('dojo.getAttr(W[%d][%d].parentNode,"widgetid")',ind1,ind2)); + error('Bailout!'); + end + end + end + catch + if strcmp(tmp.type,'matlab.ui.container.TreeNode') + tmp = jsondecode(win.executeJS(sprintf(... + 'dojo.byId(%s).childNodes[0].childNodes[0].childNodes[0].childNodes[%d].dataset',... + ID(2:end-1),ind2-1))); + ID_struct = struct('ID_attr', 'data-reactid', 'ID_val', tmp.reactid); + end + % do nothing - bailout. + end end + end % getWidgetID function to = getTimeout(hFig) From b8735a130726f355aefc2ed816f9642ed79d3d3b Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Thu, 4 Jan 2018 11:50:20 +0200 Subject: [PATCH 08/14] Added the WidgetID data class for better organization. --- WidgetID.m | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 WidgetID.m diff --git a/WidgetID.m b/WidgetID.m new file mode 100644 index 0000000..35712df --- /dev/null +++ b/WidgetID.m @@ -0,0 +1,17 @@ +classdef WidgetID < handle + % A data class for storing identifying information about JS widgets in UIFigures. + + properties (GetAccess = public, SetAccess = public) + ID_attr char + ID_val char + end + + methods + % Counstructor: + function obj = WidgetID(identifyingAttributeName, identifyingAttributeValue) + obj.ID_attr = identifyingAttributeName; + obj.ID_val = identifyingAttributeValue; + end + end + +end \ No newline at end of file From e3af320fa8715bf3b1a0a0784989b2da40016c49 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Thu, 4 Jan 2018 12:09:16 +0200 Subject: [PATCH 09/14] Refactoring - Cleaned up getWidgetID: separated some experimental fallback functionality into getWidgetIDFromDijit. - Made use of the WidgetID class where appropriate. - Added a warning for unsupported UI objects. - Removed incorrect comment. - Added some useful debugging code at the end. - TODO: make uniform indentation width (2 or 4). - TODO: be consistent instead of "hWin, win, hWebwindow" etc. --- mlapptools.m | 102 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 40 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 7751eea..d3dd8e7 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -96,13 +96,23 @@ function fontWeight(uiElement, weight) end % getHTML function [win, widgetID] = getWebElements(uiElement) - % A method for obtaining the webwindow handle and the widgetID corresponding + % A method for obtaining the webwindow handle and the widget ID corresponding % to the provided uifigure control. % Get a handle to the webwindow win = mlapptools.getWebWindow(uiElement); + mlapptools.waitTillWebwindowLoaded(win); % Find which element of the DOM we want to edit - widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); + switch uiElement.Type + case 'uitreenode' + p = uiElement.Parent; + if ~isa(p,'matlab.ui.container.Tree') + p.expand(); % The row must be visible to apply changes + end + widgetID = WidgetID('data-test-id', char(struct(uiElement).NodeId)); + otherwise % default: + widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); + end end % getWebElements function [win] = getWebWindow(hUIObj) @@ -198,7 +208,7 @@ function fontWeight(uiElement, weight) % 3-parameter call: % widgetID = setStyle(hControl, styleAttr, styleValue) % 4-parameter call: - % setStyle(hWin, styleAttr, styleValue, ID_struct) + % setStyle(hWin, styleAttr, styleValue, ID_obj) narginchk(3,4); % Unpack inputs: @@ -209,15 +219,15 @@ function fontWeight(uiElement, weight) case 3 hControl = varargin{1}; % Get a handle to the webwindow - [win, ID_struct] = mlapptools.getWebElements(hControl); + [win, ID_obj] = mlapptools.getWebElements(hControl); case 4 - % If we know the ID_struct, the webwindow handle must be available + % By the time we have a WidgetID object, the webwindow handle is available win = varargin{1}; - ID_struct = varargin{4}; + ID_obj = varargin{4}; end styleSetStr = sprintf('dojo.style(dojo.query("[%s = ''%s'']")[0], "%s", "%s")',... - ID_struct.ID_attr, ID_struct.ID_val, styleAttr, styleValue); + ID_obj.ID_attr, ID_obj.ID_val, styleAttr, styleValue); % ^ this might result in junk if widgetId=='null'. try win.executeJS(styleSetStr); @@ -230,7 +240,7 @@ function fontWeight(uiElement, weight) % Assign outputs: if nargout >= 1 - varargout{1} = ID_struct; + varargout{1} = ID_obj; end end % setStyle @@ -349,47 +359,52 @@ function textAlign(uiElement, alignment) hFig = hFigs(hWebwindow == ww); end % figFromWebwindow - function [ID_struct] = getWidgetID(win, data_tag) + function [ID_obj] = getWidgetID(win, data_tag) % This method returns a structure containing some uniquely-identifying information % about a DOM node. widgetquerystr = sprintf('dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag); - mlapptools.waitTillWebwindowLoaded(win); try % should work for most UI objects ID = win.executeJS(widgetquerystr); - ID_struct = struct('ID_attr', mlapptools.DEF_ID_ATTRIBUTE, 'ID_val', ID(2:end-1)); + ID_obj = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, ID(2:end-1)); catch % fallback for problematic objects - % We retry by using the dijit registry: - win.executeJS(['var W; require(["dijit/registry"], '... - 'function(registry){W = registry.toArray().map(x => x.domNode.childNodes);});']); - nWidgets = jsondecode(win.executeJS('W.length')); - try - for ind1 = 0:nWidgets-1 - nChild = jsondecode(win.executeJS(sprintf('W[%d].length',ind1))); - for ind2 = 0:nChild-1 - tmp = win.executeJS(sprintf('W[%d][%d].dataset',ind1,ind2)); - if isempty(tmp) - continue - else - tmp = jsondecode(tmp); - end - if isfield(tmp,'tag') && strcmp(tmp.tag,data_tag) - ID = win.executeJS(sprintf('dojo.getAttr(W[%d][%d].parentNode,"widgetid")',ind1,ind2)); - error('Bailout!'); - end - end + warning('This widget is unsupported.'); +% ID_obj = mlapptools.getWidgetIDFromDijit(win, data_tag); + end + end % getWidgetID + + function ID_obj = getWidgetIDFromDijit(win, data_tag) + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% EXPERIMENTAL METHOD!!! + win.executeJS(['var W; require(["dijit/registry"], '... + 'function(registry){W = registry.toArray().map(x => x.domNode.childNodes);});']); + nWidgets = jsondecode(win.executeJS('W.length')); + try + for ind1 = 0:nWidgets-1 + nChild = jsondecode(win.executeJS(sprintf('W[%d].length',ind1))); + for ind2 = 0:nChild-1 + tmp = win.executeJS(sprintf('W[%d][%d].dataset',ind1,ind2)); + if isempty(tmp) + continue + else + tmp = jsondecode(tmp); end - catch - if strcmp(tmp.type,'matlab.ui.container.TreeNode') - tmp = jsondecode(win.executeJS(sprintf(... - 'dojo.byId(%s).childNodes[0].childNodes[0].childNodes[0].childNodes[%d].dataset',... - ID(2:end-1),ind2-1))); - ID_struct = struct('ID_attr', 'data-reactid', 'ID_val', tmp.reactid); + if isfield(tmp,'tag') && strcmp(tmp.tag,data_tag) + ID = win.executeJS(sprintf('dojo.getAttr(W[%d][%d].parentNode,"widgetid")',ind1,ind2)); + error('Bailout!'); end - % do nothing - bailout. end end - - end % getWidgetID + ID_obj = WidgetID('',''); + catch + % Fix for the case of top-level tree nodes: + switch tmp.type + case 'matlab.ui.container.TreeNode' + tmp = jsondecode(win.executeJS(sprintf(... + 'dojo.byId(%s).childNodes[0].childNodes[0].childNodes[0].childNodes[%d].dataset',... + ID(2:end-1),ind2-1))); + ID_obj = WidgetID('data-reactid', tmp.reactid); + end + end + end % getWidgetIDFromDijit function to = getTimeout(hFig) to = getappdata(hFig, mlapptools.TAG_TIMEOUT); @@ -523,4 +538,11 @@ function waitTillWebwindowLoaded(hWebwindow, hFig) end % Private Static Methods -end % classdef \ No newline at end of file +end % classdef + +%{ +Useful debugging code: + +jsprops = sort(jsondecode(win.executeJS('Object.keys(this)'))); + +%} \ No newline at end of file From bc351ea72a63df157a23e3c1cd28a5c7ef102a68 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Thu, 4 Jan 2018 18:46:56 +0200 Subject: [PATCH 10/14] [Breaking] Renamed and expanded aboutDojo() - Method is now known as aboutJSLibs(). - Method now also returns the ReactJS version. - Dojo version is now returned as a string (or on older versions, a JSON-stripped char vector). - Added some more debugging commands for exploring React. --- mlapptools.m | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index d3dd8e7..fc2c3f1 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -5,7 +5,7 @@ % % MLAPPTOOLS' public methods: % - % aboutDojo - Return the dojo toolkit version. + % aboutJSLibs - Return version information about certain JS libraries. % fontColor - Modify font color. % fontWeight - Modify font weight. % getHTML - Return the full HTML code of a uifigure. @@ -29,8 +29,9 @@ methods (Access = public, Static = true) - function [dojoVersion] = aboutDojo() - % A method for getting version info about the Dojo Toolkit visible by MATLAB. + function [jsLibVersions] = aboutJSLibs() + % A method for getting version info about some JS libararies visible to MATLAB. + % This includes the Dojo Toolkit and ReactJS. if ~numel(matlab.internal.webwindowmanager.instance.findAllWebwindows()) f=uifigure; drawnow; tmpWindowCreated = true; @@ -40,15 +41,26 @@ dojoVersion = matlab.internal.webwindowmanager.instance ... .windowList(1).executeJS('dojo.version'); - + + reactVersion = matlab.internal.webwindowmanager.instance ... + .windowList(1).executeJS(... + 'require("react/react.min").version;'); if tmpWindowCreated delete(f); end + % If MATLAB is sufficiently new, convert the JSON to a struct: if str2double(subsref(ver('matlab'), substruct('.','Version'))) >= 9.1 %R2016b - dojoVersion = jsondecode(dojoVersion); + dv = jsondecode(dojoVersion); + dojoVersion = strrep(join(string(struct2cell(dv)),'.'),'..','.'); + reactVersion = jsondecode(reactVersion); + else + dojoVersion = strsplit(dojoVersion,{':',',','"','}','{'}); + dojoVersion = char(strjoin(dojoVersion([3,5,7,10]),'.')); + reactVersion = reactVersion(2:end-1); end - end % aboutDojo + jsLibVersions = struct('dojo', dojoVersion, 'react_js', reactVersion); + end % aboutJSLibs function fontColor(uiElement, newcolor) % A method for manipulating text color. @@ -541,8 +553,13 @@ function waitTillWebwindowLoaded(hWebwindow, hFig) end % classdef %{ -Useful debugging code: +--- Useful debugging commands --- jsprops = sort(jsondecode(win.executeJS('Object.keys(this)'))); +ReactJS: +win.executeJS('var R = require("react/react.min"); Object.keys(R)') +win.executeJS('var R = require("react/react-dom.min"); Object.keys(R)') + + %} \ No newline at end of file From 6084c26f70c8c08e09bba28b80195a58a1493859 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Sat, 6 Jan 2018 13:24:13 +0200 Subject: [PATCH 11/14] Added special treatment for uipanel objects This addresses issue #9. --- mlapptools.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mlapptools.m b/mlapptools.m index fc2c3f1..3fb8b73 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -122,6 +122,8 @@ function fontWeight(uiElement, weight) p.expand(); % The row must be visible to apply changes end widgetID = WidgetID('data-test-id', char(struct(uiElement).NodeId)); + case 'uipanel' + widgetID = WidgetID('data-tag', mlapptools.getDataTag(uiElement)); otherwise % default: widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); end From 14f6313f5aba07a09402ae63bf0b0beb92455120 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Sun, 7 Jan 2018 12:14:21 +0200 Subject: [PATCH 12/14] Updated documentation. - Added information about new/changed methods. - Added breaks (----) to improve readability. - Modified some aspects of the document according to the conventions of https://github.com/DavidAnson/markdownlint/blob/v0.6.4/doc/Rules.md . - aboutJSLibs() - Dojo version is now returned as a character array for newer MATLAB versions too. --- README.md | 188 ++++++++++++++++++++++++++++++++++----------------- mlapptools.m | 2 +- 2 files changed, 127 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 371ce45..17c582d 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,83 @@ ![Minimum Version](https://img.shields.io/badge/Requires-R2016a%20%28v9.0%29%20or%20newer-orange.svg) # mlapptools -`mlapptools` is a collection of static methods for customizing the R2016a-introduced web-based `uifigure` windows and -associated UI elements through DOM manipulations. -For additional information, see Iliya Romm's Undocumented Matlab guest article, -[*Customizing uifigures part 2*](http://undocumentedmatlab.com/blog/customizing-uifigures-part-2), +`mlapptools` is a collection of static methods for customizing the R2016a-introduced web-based `uifigure` windows and +associated UI elements through DOM manipulations. + +For additional information, see Iliya Romm's Undocumented Matlab guest article, +[*Customizing uifigures part 2*](http://undocumentedmatlab.com/blog/customizing-uifigures-part-2), published Wednesday, September 7th, 2016. #### Contact us -- Feature requests/suggestions and bug repots - please feel free to + +- Feature requests/suggestions and bug repots - please feel free to [open an issue](https://github.com/StackOverflowMATLABchat/mlapptools/issues) in the repository. - General discussion - [_MATLAB and Octave_ chatroom on Stack Overflow](https://chat.stackoverflow.com/rooms/81987/). -- Specific questions about manipulating uifigures / App Designer apps - +- Specific questions about manipulating uifigures / App Designer apps - [`matlab-app-designer` tag on Stack Overflow](https://stackoverflow.com/questions/tagged/matlab-app-designer). - + ## Methods -[`aboutDojo`](#aboutDojo) - Return the dojo toolkit version. -[`fontColor`](#fontColor) - Modify font color. -[`fontWeight`](#fontWeight) - Modify font weight. -[`getHTML`](#getHTML) - Return the full HTML code of a `uifigure`. -[`getWebElements`](#getWebElements) - Extract a `webwindow` handle and a widget ID from a `uifigure` control handle. -[`getWebWindow`](#getWebWindow) - Extract a `webwindow` handle from a `uifigure` handle. -[`getWidgetInfo`](#getWidgetInfo) - Gather information about a specific dijit widget. -[`getWidgetList`](#getWidgetList) - Gather information about all dijit widget in a specified `uifigure`. -[`setStyle`](#setStyle) - Modify a specified style property. -[`setTimeout`](#setTimeout) - Override the default timeout for dojo commands, for a specific `uifigure`. -[`textAlign`](#textAlign) - Modify text alignment. - - -#### *mlapptools*.**aboutDojo**() + +[`aboutJSLibs`](#aboutJSLibs) - Return the loaded versions of Dojo and React. +[`fontColor`](#fontColor) - Modify font color. +[`fontWeight`](#fontWeight) - Modify font weight. +[`getHTML`](#getHTML) - Return the full HTML code of a `uifigure`. +[`getWebElements`](#getWebElements) - Extract a `webwindow` handle and a widget ID from a `uifigure` control handle. +[`getWebWindow`](#getWebWindow) - Extract a `webwindow` handle from a `uifigure` handle. +[`getWidgetInfo`](#getWidgetInfo) - Gather information about a specific dijit widget. +[`getWidgetList`](#getWidgetList) - Gather information about all dijit widget in a specified `uifigure`. +[`setStyle`](#setStyle) - Modify a specified style property. +[`setTimeout`](#setTimeout) - Override the default timeout for dojo commands, for a specific `uifigure`. +[`textAlign`](#textAlign) - Modify text alignment. +[`waitForFigureReady`](#waitForFigureReady) - A blocking method that only returns after the uifigure is fully loaded. + +----------------- + + +#### *mlapptools*.**aboutJSLibs**() + ##### Description -Returns a `struct` containing version information about the Dojo toolkit loaded into the first open `uifigure`. -If no `uifigure` is open, a temporary window is created, queried, then closed - indicating the default Dojo version. + +Returns a `struct` containing information about the [Dojo toolkit](https://dojotoolkit.org/) and +[ReactJS](https://reactjs.org/) versions loaded into the first open `uifigure`. +If no `uifigure` is open, a temporary window is created, queried, then closed - indicating the default versions. ##### Examples -```matlab ->> mlapptools.aboutDojo() +```MATLAB +>> mlapptools.aboutJSLibs() -ans = +ans = struct with fields: - major: 1 - minor: 11 - patch: 2 - flag: '' - revision: '91fa0cb' + dojo: '1.11.2.91fa0cb' + react_js: '0.14.7' ``` +----------------- + #### *mlapptools*.**fontColor**(*uiElement*, *newcolor*) + ##### Description + Set the font color of the specified UI element, `'uiElement'`, to the input color, `newcolor`. `newcolor` can be a predefined color string or a string containing a valid CSS color method call. Valid color specifications are: - * Hexadecimal colors - e.g. `'#ff0000'` for red - * RGB colors - e.g. `'rgb(255,165,0)'` for orange - * RGBA colors - e.g. `'rgba(255,255,0,0.3)'` for yellow - * HSL colors - e.g. `'hsl(120, 100%, 50%)'` for green - * HSLA colors - e.g. `'hsla(240,100%,50%, 1.0)'` for blue - * Predefined color names - e.g. `'red'`, `'orange'`, `'yellow'`, `'green'`, `'blue'`, `'indigo'`, `'violet'`. + +- Hexadecimal colors - e.g. `'#ff0000'` for red +- RGB colors - e.g. `'rgb(255,165,0)'` for orange +- RGBA colors - e.g. `'rgba(255,255,0,0.3)'` for yellow +- HSL colors - e.g. `'hsl(120, 100%, 50%)'` for green +- HSLA colors - e.g. `'hsla(240,100%,50%, 1.0)'` for blue +- Predefined color names - e.g. `'red'`, `'orange'`, `'yellow'`, `'green'`, `'blue'`, `'indigo'`, `'violet'`. For more colors, see the predefined color names [CSS color specification](https://www.w3.org/TR/css3-color/). ##### Examples + Using the demo GUI generated by `./Demo/DOMdemoGUI.m` ```MATLAB @@ -78,22 +90,27 @@ myGUI = DOMdemoGUI; mlapptools.fontColor(myGUI.TextArea, 'rgb(255,165,0)'); ``` - +----------------- + #### *mlapptools*.**fontWeight**(*uiElement*, *weight*) + ##### Description + Set the font weight of the specified UI element, `uiElement`, to the input weight string or integer, `weight`. For this setting to have an effect, the font being used must have built-in faces that match the specified weight. Valid font weight property values are: - * `'normal'` - Normal characters (default) - * `'bold'` - Thick characters - * `'lighter'` / `'bolder'` - The closest available "lighter" or "bolder" weight, relative to the parent. - * `100 .. 900` - Integers mapping to `'normal'`, `'bold'`, etc. + +- `'normal'` - Normal characters (default) +- `'bold'` - Thick characters +- `'lighter'` / `'bolder'` - The closest available "lighter" or "bolder" weight, relative to the parent. +- `100 .. 900` - Integers mapping to `'normal'`, `'bold'`, etc. Intermediate integers (and floats) are accepted but generally map to the available values - * `'initial'` - Revert to default +- `'initial'` - Revert to default ##### Examples + Using the demo GUI generated by `./Demo/DOMdemoGUI.m` ```MATLAB @@ -106,125 +123,163 @@ myGUI = DOMdemoGUI; mlapptools.fontWeight(myGUI.TextArea, 600); ``` - +----------------- + #### *mlapptools*.**getHTML**(*hUIFigure*) + ##### Description + A method for obtaining the HTML code of a uifigure. Intended for R2017b (and onward?) where the CEF URL cannot be simply opened in a browser. The returned HTML is a deep copy, meaning that any changes will **not** modify the `uifigure` where it originated from. ##### Examples + Using the demo GUI generated by `./Demo/DOMdemoGUI.m` + ```MATLAB myGUI = DOMdemoGUI; fullHTML = mlapptools.getHTML(myGUI.UIFigure); web(['text://' fullHTML]); ``` - +----------------- + #### *mlapptools*.**getWebElements**(*uiElement*) + ##### Description -A method for obtaining the webwindow handle and the widget ID corresponding to the provided `uifigure` control. The + +A method for obtaining the webwindow handle and the widget ID corresponding to the provided `uifigure` control. The widget ID can be used to call the 4-parameter variant of [`setStyle`](#setStyle). ##### Examples + ```MATLAB myGUI = DOMdemoGUI; [win, widgetID] = mlapptools.getWebElements(myGUI.TextArea); ``` - +----------------- + #### *mlapptools*.**getWebWindow**(*hUIFigure*) + ##### Description + A method for getting the webwindow handle associated with the provided `uifigure`. ##### Examples + Using the demo GUI generated by `./Demo/DOMdemoGUI.m` + ```MATLAB myGUI = DOMdemoGUI; win = mlapptools.getWebWindow(myGUI.UIFigure); ``` - +----------------- + #### *mlapptools*.**getWidgetInfo**(*hWebwindow*, *widgetID*, *verbosityFlag*) + ##### Description -Query the [dijit registry](https://dojotoolkit.org/reference-guide/dijit/registry.html#dijit-registry) for a widgets + +Query the [dijit registry](https://dojotoolkit.org/reference-guide/dijit/registry.html#dijit-registry) for a widget having a specific ID, and return its details in a scalar `cell` containing a `struct`. ##### Examples + Using the demo GUI generated by `./Demo/DOMdemoGUI.m` + ```MATLAB myGUI = DOMdemoGUI; [win, widgetID] = mlapptools.getWebElements(myGUI.TextArea); nfo = mlapptools.getWidgetInfo(win, widgetID); ``` - +----------------- + #### *mlapptools*.**getWidgetList**(*hUIFigure*, *verbosityFlag*) + ##### Description + Query the [dijit registry](https://dojotoolkit.org/reference-guide/dijit/registry.html#dijit-registry) for all widgets within the current page, and return them in a cell array of structs. ##### Examples Using the demo GUI generated by `./Demo/DOMdemoGUI.m` + ```MATLAB myGUI = DOMdemoGUI; nfoList = mlapptools.getWidgetList(myGUI.UIFigure); ``` - +----------------- + #### *mlapptools*.**setStyle**(*uiElement*, *styleAttr*, *styleValue*) + +#### *mlapptools*.**setStyle**(*hWin*, *styleAttr*, *styleValue*, *ID_object*) + ##### Description + Set the style attribute `styleAttr` of the specified UI element, `'uiElement'`, to the value `styleValue`. `styleAttr` should be any valid CSS attribute, and `styleValue` a valid setting thereof. -This method provides a general interface to change CSS style attributes, with minimal input testing and error reporting, +This method provides a general interface to change CSS style attributes, with minimal input testing and error reporting, so it is up to the user to provide valid inputs. Valid style attributes and corresponding settings can be found [here](https://www.w3schools.com/cssref/). ##### Examples + Using the demo GUI generated by `./Demo/DOMdemoGUI.m` + ```MATLAB myGUI = DOMdemoGUI; mlapptools.setStyle(myGUI.TextArea, 'background-image',... 'url(https://upload.wikimedia.org/wikipedia/commons/8/80/Wikipedia-logo-v2.svg)'); ``` - +----------------- + #### *mlapptools*.**setTimeout**(*hUIFig*) + ##### Description + Modify the amount of time allotted to dojo queries before they are considered "failed due to timeout". This value is `uifigure`-specific. If left unmodified, the default value is `5` seconds. ##### Examples + ```MATLAB myGUI = DOMdemoGUI; mlapptools.setTimeout(myGUI.UIFigure, 3); % This will wait less for dojo queries to finish. ``` - +----------------- + #### *mlapptools*.**textAlign**(*uiElement*, *alignment*) + ##### Description -Set the horizontal text alignment of the specified UI element, `uiElement`, to that specified by the input alignment -string, `alignment`. +Set the horizontal text alignment of the specified UI element, `uiElement`, to that specified by the input alignment +string, `alignment`. Valid alignment strings are: - * `'left'` - Left align (default) - * `'center'` - Center align - * `'right'` - Right align - * `'justify'` - Each line has equal width - * `'initial'` - Revert to default + +- `'left'` - Left align (default) +- `'center'` - Center align +- `'right'` - Right align +- `'justify'` - Each line has equal width +- `'initial'` - Revert to default ##### Examples + Using the demo GUI generated by `./Demo/DOMdemoGUI.m` ```MATLAB @@ -235,4 +290,13 @@ mlapptools.textAlign(myGUI.TextArea, 'center'); ```MATLAB myGUI = DOMdemoGUI; mlapptools.textAlign(myGUI.TextArea, 'right'); -``` \ No newline at end of file +``` + +----------------- + + +#### *mlapptools*.**waitForFigureReady**(*hUIFig*) + +##### Description + +This method pauses execution of further commands on the MATLAB thread until the asynchronous loading of the UIFigure and its webwindow has finished. It's good practice to use this method before starting to manipulate the styles of individual elements to avoid `null` widget reference errors. \ No newline at end of file diff --git a/mlapptools.m b/mlapptools.m index 3fb8b73..9d2520b 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -52,7 +52,7 @@ % If MATLAB is sufficiently new, convert the JSON to a struct: if str2double(subsref(ver('matlab'), substruct('.','Version'))) >= 9.1 %R2016b dv = jsondecode(dojoVersion); - dojoVersion = strrep(join(string(struct2cell(dv)),'.'),'..','.'); + dojoVersion = char(strrep(join(string(struct2cell(dv)),'.'),'..','.')); reactVersion = jsondecode(reactVersion); else dojoVersion = strsplit(dojoVersion,{':',',','"','}','{'}); From 370e3da7c503fa10560ba6a87031d363bcd42202 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Sun, 7 Jan 2018 12:21:13 +0200 Subject: [PATCH 13/14] Fix to documentation (Reverted the implmented suggestion to remove double whitespace line endings @ Methods list) --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 17c582d..0a71998 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,17 @@ published Wednesday, September 7th, 2016. ## Methods -[`aboutJSLibs`](#aboutJSLibs) - Return the loaded versions of Dojo and React. -[`fontColor`](#fontColor) - Modify font color. -[`fontWeight`](#fontWeight) - Modify font weight. -[`getHTML`](#getHTML) - Return the full HTML code of a `uifigure`. -[`getWebElements`](#getWebElements) - Extract a `webwindow` handle and a widget ID from a `uifigure` control handle. -[`getWebWindow`](#getWebWindow) - Extract a `webwindow` handle from a `uifigure` handle. -[`getWidgetInfo`](#getWidgetInfo) - Gather information about a specific dijit widget. -[`getWidgetList`](#getWidgetList) - Gather information about all dijit widget in a specified `uifigure`. -[`setStyle`](#setStyle) - Modify a specified style property. -[`setTimeout`](#setTimeout) - Override the default timeout for dojo commands, for a specific `uifigure`. -[`textAlign`](#textAlign) - Modify text alignment. +[`aboutJSLibs`](#aboutJSLibs) - Return the loaded versions of Dojo and React. +[`fontColor`](#fontColor) - Modify font color. +[`fontWeight`](#fontWeight) - Modify font weight. +[`getHTML`](#getHTML) - Return the full HTML code of a `uifigure`. +[`getWebElements`](#getWebElements) - Extract a `webwindow` handle and a widget ID from a `uifigure` control handle. +[`getWebWindow`](#getWebWindow) - Extract a `webwindow` handle from a `uifigure` handle. +[`getWidgetInfo`](#getWidgetInfo) - Gather information about a specific dijit widget. +[`getWidgetList`](#getWidgetList) - Gather information about all dijit widget in a specified `uifigure`. +[`setStyle`](#setStyle) - Modify a specified style property. +[`setTimeout`](#setTimeout) - Override the default timeout for dojo commands, for a specific `uifigure`. +[`textAlign`](#textAlign) - Modify text alignment. [`waitForFigureReady`](#waitForFigureReady) - A blocking method that only returns after the uifigure is fully loaded. ----------------- From cf496a9b6cd705d3e83a00cea3a3ea13c1070405 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Sun, 7 Jan 2018 20:38:14 +0200 Subject: [PATCH 14/14] Added warning toggle @ getWebElements() As per review comment. --- mlapptools.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mlapptools.m b/mlapptools.m index 9d2520b..c2b7811 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -121,7 +121,9 @@ function fontWeight(uiElement, weight) if ~isa(p,'matlab.ui.container.Tree') p.expand(); % The row must be visible to apply changes end + warnState = mlapptools.toggleWarnings('off'); widgetID = WidgetID('data-test-id', char(struct(uiElement).NodeId)); + warning(warnState); % Restore warning state case 'uipanel' widgetID = WidgetID('data-tag', mlapptools.getDataTag(uiElement)); otherwise % default: