From acbaa18153dbe5b11f354a8f218ce680ff2b6bf6 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 9 Jan 2018 12:14:25 +0200 Subject: [PATCH 1/3] Added preallocation support to WidgetID - Made the internal documentation more accurate. --- WidgetID.m | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/WidgetID.m b/WidgetID.m index 35712df..b91c62f 100644 --- a/WidgetID.m +++ b/WidgetID.m @@ -1,14 +1,23 @@ classdef WidgetID < handle - % A data class for storing identifying information about JS widgets in UIFigures. + % A data class for storing identifying information about HTML DOM nodes in UIFigures. + % When the identifying attribute (ID_attr) is "widgetid", this points to a dijit widget. properties (GetAccess = public, SetAccess = public) ID_attr char ID_val char end + properties (Access = private, Constant = true) + DEF_PROP_VAL = ''; + end + methods % Counstructor: function obj = WidgetID(identifyingAttributeName, identifyingAttributeValue) + if nargin == 0 % Case of preallocation + identifyingAttributeName = WidgetID.DEF_PROP_VAL; + identifyingAttributeValue = WidgetID.DEF_PROP_VAL; + end obj.ID_attr = identifyingAttributeName; obj.ID_val = identifyingAttributeValue; end From ca72615c873572e88fe98146e9cb3a00f714f458 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 9 Jan 2018 12:18:46 +0200 Subject: [PATCH 2/3] Added basic support for uitabgroup and uitab Also: - Some fixes to indentation. - Minor internal comment modifications. - Added a warning to getWidgetInfo(). --- mlapptools.m | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 2afd2c5..c76a284 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -124,7 +124,7 @@ function fontWeight(uiElement, weight) warnState = mlapptools.toggleWarnings('off'); widgetID = WidgetID('data-test-id', char(struct(uiElement).NodeId)); warning(warnState); % Restore warning state - case {'uipanel','figure'} + case {'uipanel','figure','uitabgroup','uitab'} widgetID = WidgetID('data-tag', mlapptools.getDataTag(uiElement)); otherwise % default: widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); @@ -157,6 +157,12 @@ function fontWeight(uiElement, weight) function [nfo] = getWidgetInfo(win, widgetID, verboseFlag) % A method for gathering information about a specific dijit widget, if its % HTML div id is known. + if ~strcmp(widgetID.ID_attr,'widgetid') + warning('getWidgetInfo:InappropriateIDAttribute',... + 'This method requires widgets identified by a ''widgetid'' attribute.'); + nfo = struct([]); + return + end %% Handling required positional inputs: assert(nargin >= 2,'mlapptools:getWidgetInfo:insufficientInputs',... 'getWidgetInfo must be called with at least 2 inputs.'); @@ -209,8 +215,7 @@ function fontWeight(uiElement, weight) if nargout == 2 % Convert to a single table: varargout{2} = struct2table(mlapptools.unifyStructs(widgets)); - end % getWidgetInfo - + end % getWidgetList end function varargout = setStyle(varargin) @@ -257,13 +262,12 @@ function fontWeight(uiElement, weight) % Assign outputs: if nargout >= 1 varargout{1} = ID_obj; - end - + end end % setStyle function setTimeout(hUIFig, newTimeoutInSec) - % Sets a custom timeout for dojo queries, specified in [s]. - setappdata(hUIFig, mlapptools.TAG_TIMEOUT, newTimeoutInSec); + % Sets a custom timeout for dojo queries, specified in [s]. + setappdata(hUIFig, mlapptools.TAG_TIMEOUT, newTimeoutInSec); end function textAlign(uiElement, alignment) @@ -309,8 +313,9 @@ function textAlign(uiElement, alignment) end end % checkJavascriptSyntaxError - function widgets = decodeDijitRegistryResult(win, verboseFlag) - assert(jsondecode(win.executeJS(... + function widgets = decodeDijitRegistryResult(win, verboseFlag) + % As this method relies heavily on jsondecode, it is only supported on R >= 2016b + assert(strcmp('true', win.executeJS(... 'this.hasOwnProperty("W") && W !== undefined && W instanceof Array && W.length > 0')),... 'mlapptools:decodeDijitRegistryResult:noSuchWidget',... 'The dijit registry doesn''t contain the specified widgetID.'); @@ -362,23 +367,24 @@ function textAlign(uiElement, alignment) 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)); - hUIFigs = hUIFigs(strcmp({hUIFigs.Visible},'on')); % Hidden figures are ignored - ww = arrayfun(@mlapptools.getWebWindow, hUIFigs); - warning(warnState); % Restore warning state - hFig = hFigs(hWebwindow == ww); + % 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)); + hUIFigs = hUIFigs(strcmp({hUIFigs.Visible},'on')); % Hidden figures are ignored + ww = arrayfun(@mlapptools.getWebWindow, hUIFigs); + warning(warnState); % Restore warning state + hFig = hFigs(hWebwindow == ww); end % figFromWebwindow 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); + widgetquerystr = sprintf(... + 'dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag); try % should work for most UI objects ID = win.executeJS(widgetquerystr); ID_obj = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, ID(2:end-1)); From c9cbcf52bf7350ab5c34cbb8edf27924cc0feffc Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 9 Jan 2018 12:51:59 +0200 Subject: [PATCH 3/3] Added two new public methods: getChildNodeIDs, getParentNodeID These methods should allow customization of "difficilt-to-find-handles-to" DOM nodes, such as a tablist. The private `establishIdentities` method tries to generate WidgetID objects for a list of elements by using a list of attributes known (assumed?) to be unique. Addresses #12. --- README.md | 43 ++++++++++++++++++++++++++++++++++++++++ mlapptools.m | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/README.md b/README.md index 0a71998..e0eff96 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,9 @@ published Wednesday, September 7th, 2016. [`aboutJSLibs`](#aboutJSLibs) - Return the loaded versions of Dojo and React. [`fontColor`](#fontColor) - Modify font color. [`fontWeight`](#fontWeight) - Modify font weight. +[`getChildNodeIDs`](#getChildNodeIDs) - Get all children DOM nodes of a specified node. [`getHTML`](#getHTML) - Return the full HTML code of a `uifigure`. +[`getParentNodeID`](#getParentNodeID) - Get parent DOM node of a specified node. [`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. @@ -123,6 +125,27 @@ myGUI = DOMdemoGUI; mlapptools.fontWeight(myGUI.TextArea, 600); ``` +----------------- + + +#### *mlapptools*.**getChildNodeIDs**(*hWebWindow*,*widgetID*) + +##### Description + +A method for getting all children nodes (commonly `
` elements) of a specified node. Returns a WidgetID vector. + +##### Examples + +```MATLAB +tg = uitabgroup(uifigure()); +uitab(tg); +[win, widgetID] = mlapptools.getWebElements(tg); +[childIDs] = mlapptools.getChildNodeIDs(win, widgetID); +mlapptools.setStyle(win,'background','blue',childIDs(2)); +[childIDs] = mlapptools.getChildNodeIDs(win, childIDs(2)); +mlapptools.setStyle(win,'background','green',childIDs(4)); +``` + ----------------- @@ -144,6 +167,26 @@ fullHTML = mlapptools.getHTML(myGUI.UIFigure); web(['text://' fullHTML]); ``` +----------------- + + +#### *mlapptools*.**getParentNodeID**(*hWebWindow*,*widgetID*) + +##### Description + +A method for getting the parent node (commonly `
` element) of a specified node. Returns a WidgetID scalar. + +##### Examples + +Using the demo GUI generated by `./Demo/DOMdemoGUI.m` + +```MATLAB +tg = uitabgroup(uifigure()); +[win, widgetID] = mlapptools.getWebElements(tg); +mlapptools.setStyle(win,'background','linear-gradient(red, pink, white)',... + mlapptools.getParentNodeID(win, widgetID)); +``` + ----------------- diff --git a/mlapptools.m b/mlapptools.m index c76a284..c5c4631 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -81,6 +81,19 @@ function fontWeight(uiElement, weight) mlapptools.setStyle(win, 'font-weight', weight, ID_struct); end % fontWeight + function [childIDs] = getChildNodeIDs(win,ID_obj) + % A method for getting all children nodes (commonly
) of a specified node. + % Returns a vector WidgetID. + queryStr = sprintf(['var W = dojo.query("[%s = ''%s'']").map(',... + 'function(node){return node.childNodes;})[0];'],ID_obj.ID_attr, ID_obj.ID_val); + % The [0] above is required because an Array of Arrays is returned. + [~] = win.executeJS(queryStr); + % Try to establish an ID: + childIDs = mlapptools.establishIdentities(win); + % "Clear" the temporary JS variable + win.executeJS('W = undefined'); + end % getChildNodeIDs + function [fullHTML] = getHTML(hUIFig) % A method for dumping the HTML code of a uifigure. % Intended for R2017b (and onward?) where the CEF url cannot be simply opened in a browser. @@ -106,6 +119,18 @@ function fontWeight(uiElement, weight) fclose(fid); %} end % getHTML + + function [parentID] = getParentNodeID(win,ID_obj) + % A method for getting the parent node (commonly
) of a specified node. + % Returns a scalar WidgetID. + queryStr = sprintf(['var W = dojo.query("[%s = ''%s'']").map(',... + 'function(node){return node.parentNode;});'],ID_obj.ID_attr, ID_obj.ID_val); + [~] = win.executeJS(queryStr); + % Try to establish an ID: + parentID = mlapptools.establishIdentities(win); + % "Clear" the temporary JS variable + win.executeJS('W = undefined'); + end % getParentNodeID function [win, widgetID] = getWebElements(uiElement) % A method for obtaining the webwindow handle and the widget ID corresponding @@ -360,6 +385,37 @@ function textAlign(uiElement, alignment) end % emptyStructWithFields + function [widgetID] = establishIdentities(win) % throws AssertionError + % A method for generating WidgetID objects from a list of DOM nodes. + assert(strcmp('true', win.executeJS([... + 'this.hasOwnProperty("W") && W !== undefined && ' ... + '(W instanceof NodeList || W instanceof Array) && W.length > 0'])),... + 'mlapptools:establishIdentities:noSuchNode',... + 'No nodes meet the condition.'); + + attrs = {'widgetid', 'id', 'data-tag', 'data-reactid'}; + nA = numel(attrs); + %% Preallocate output: + widgetID(win.executeJS('W.length') - '0', 1) = WidgetID; % "str2double" + %% + for indW = 1:numel(widgetID) + for indA = 1:nA + % Get the attribute value: + ID = win.executeJS(sprintf('W[%d].getAttribute("%s")', indW-1, attrs{indA})); + % Test result validity: + if ~strcmp(ID,'null') + % Create a WidgetID object and proceed to the next element in W: + widgetID(indW) = WidgetID(attrs{indA}, ID(2:end-1)); + break + end + if indA == nA + % Reaching this point means that the break didn't trigger for any of the attributes. + warning('The node''s ID could not be established using common attributes.'); + end + end + end + end % establishIdentity + function [data_tag] = getDataTag(uiElement) warnState = mlapptools.toggleWarnings('off'); data_tag = char(struct(uiElement).Controller.ProxyView.PeerNode.getId);