diff --git a/deploy/Toolbox Packaging Project.prj b/deploy/Toolbox Packaging Project.prj index 829328d..9696a27 100644 --- a/deploy/Toolbox Packaging Project.prj +++ b/deploy/Toolbox Packaging Project.prj @@ -1,8 +1,8 @@ Widgets Toolbox - MATLAB App Building Components - Robyn Jackey - rjackey@mathworks.com + MathWorks Consulting + consulting@mathworks.com MathWorks Consulting Additional app building components to efficiently develop advanced user interfaces in MATLAB Widgets Toolbox helps you efficiently develop advanced user interfaces in MATLAB. Widgets combine existing control functionalities together into larger, reusable, common functionality to accelerate development of graphical user interfaces. @@ -28,7 +28,7 @@ https://www.mathworks.com/matlabcentral/fileexchange/66235-widgets-toolbox Planning a complex or business-critical app? MathWorks Consulting can advise you on design and architecture: https://www.mathworks.com/services/consulting/proven-solutions/software-development-with-matlab.html ${PROJECT_ROOT}\widgets_logo.png - 2.0.2 + 2.0.3 ${PROJECT_ROOT}\Widgets Toolbox - MATLAB App Building Components.mltbx MATLAB diff --git a/issues/abortset_update_bug/MyComponent.m b/issues/abortset_update_bug/MyComponent.m new file mode 100644 index 0000000..b12d4eb --- /dev/null +++ b/issues/abortset_update_bug/MyComponent.m @@ -0,0 +1,40 @@ +classdef MyComponent < matlab.ui.componentcontainer.ComponentContainer + + %% Public properties + properties (AbortSet) + + Name (1,1) string = "My Component" + + end %properties + + + %% Internal Properties + properties (Transient, NonCopyable, Access = protected) + + Label matlab.ui.control.Label + + end %properties + + + %% Protected methods + methods (Access = protected) + + function setup(obj) + + obj.Position(3:4) = [200 25]; + grid = uigridlayout(obj,[1 1],'Padding',[0 0 0 0]); + obj.Label = uilabel(grid,'BackgroundColor','green'); + + end %function + + + function update(obj) + + disp("MyComponent update called"); + obj.Label.Text = obj.Name; + + end %function + + end %methods + +end %classdef \ No newline at end of file diff --git a/issues/toolbar_pulldown_bug/reproduceToolbarBug.m b/issues/toolbar_pulldown_bug/reproduceToolbarBug.m new file mode 100644 index 0000000..cb206cd --- /dev/null +++ b/issues/toolbar_pulldown_bug/reproduceToolbarBug.m @@ -0,0 +1,23 @@ +f = uifigure; +g = uigridlayout(f,[2 1]); +g.RowHeight = {80, '1x'}; + +tb = wt.Toolbar(g); +dummy = uipanel(g,'BackgroundColor','red'); + +tb.Section = [ + addSection() + addSection() + addSection() + addSection() + ]; + + +function s = addSection() + +s = wt.toolbar.HorizontalSection(); +for idx = 1:4 + s.addButton("", string(idx)); +end + +end \ No newline at end of file diff --git a/license.txt b/license.txt index a936a61..ae77a6a 100644 --- a/license.txt +++ b/license.txt @@ -1,4 +1,4 @@ -Copyright (c) 2020, The MathWorks, Inc. +Copyright (c) 2020-2021, The MathWorks, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/release/Widgets Toolbox 2.0.3.mltbx b/release/Widgets Toolbox 2.0.3.mltbx new file mode 100644 index 0000000..0cb418e Binary files /dev/null and b/release/Widgets Toolbox 2.0.3.mltbx differ diff --git a/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/RlHaUdWFi_lNvfF2h5hyAf4K1x8d.xml b/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/RlHaUdWFi_lNvfF2h5hyAf4K1x8d.xml new file mode 100644 index 0000000..80b5b16 --- /dev/null +++ b/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/RlHaUdWFi_lNvfF2h5hyAf4K1x8d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/RlHaUdWFi_lNvfF2h5hyAf4K1x8p.xml b/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/RlHaUdWFi_lNvfF2h5hyAf4K1x8p.xml new file mode 100644 index 0000000..804df77 --- /dev/null +++ b/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/RlHaUdWFi_lNvfF2h5hyAf4K1x8p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/mbVnexFlSS9dIdTgdsJ6R-P4Fkkd.xml b/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/mbVnexFlSS9dIdTgdsJ6R-P4Fkkd.xml new file mode 100644 index 0000000..1c0844e --- /dev/null +++ b/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/mbVnexFlSS9dIdTgdsJ6R-P4Fkkd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/mbVnexFlSS9dIdTgdsJ6R-P4Fkkp.xml b/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/mbVnexFlSS9dIdTgdsJ6R-P4Fkkp.xml new file mode 100644 index 0000000..5199d61 --- /dev/null +++ b/resources/project/96GeO6L1DdRMBtEBPq3bpWosruk/mbVnexFlSS9dIdTgdsJ6R-P4Fkkp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/_ymR1xA4ytc1MSjw6Zd2o0l9Uq8d.xml b/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/_ymR1xA4ytc1MSjw6Zd2o0l9Uq8d.xml new file mode 100644 index 0000000..80b5b16 --- /dev/null +++ b/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/_ymR1xA4ytc1MSjw6Zd2o0l9Uq8d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/_ymR1xA4ytc1MSjw6Zd2o0l9Uq8p.xml b/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/_ymR1xA4ytc1MSjw6Zd2o0l9Uq8p.xml new file mode 100644 index 0000000..793d616 --- /dev/null +++ b/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/_ymR1xA4ytc1MSjw6Zd2o0l9Uq8p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/rtt8ZXOqSTyueG7_ypu5JtofCuEd.xml b/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/rtt8ZXOqSTyueG7_ypu5JtofCuEd.xml new file mode 100644 index 0000000..1c0844e --- /dev/null +++ b/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/rtt8ZXOqSTyueG7_ypu5JtofCuEd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/rtt8ZXOqSTyueG7_ypu5JtofCuEp.xml b/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/rtt8ZXOqSTyueG7_ypu5JtofCuEp.xml new file mode 100644 index 0000000..5199d61 --- /dev/null +++ b/resources/project/9ENWrIOXxrb6YaitWVPAXr1h5jE/rtt8ZXOqSTyueG7_ypu5JtofCuEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/96GeO6L1DdRMBtEBPq3bpWosrukd.xml b/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/96GeO6L1DdRMBtEBPq3bpWosrukd.xml new file mode 100644 index 0000000..1c0844e --- /dev/null +++ b/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/96GeO6L1DdRMBtEBPq3bpWosrukd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/96GeO6L1DdRMBtEBPq3bpWosrukp.xml b/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/96GeO6L1DdRMBtEBPq3bpWosrukp.xml new file mode 100644 index 0000000..ac8594c --- /dev/null +++ b/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/96GeO6L1DdRMBtEBPq3bpWosrukp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/9ENWrIOXxrb6YaitWVPAXr1h5jEd.xml b/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/9ENWrIOXxrb6YaitWVPAXr1h5jEd.xml new file mode 100644 index 0000000..1c0844e --- /dev/null +++ b/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/9ENWrIOXxrb6YaitWVPAXr1h5jEd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/9ENWrIOXxrb6YaitWVPAXr1h5jEp.xml b/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/9ENWrIOXxrb6YaitWVPAXr1h5jEp.xml new file mode 100644 index 0000000..630e741 --- /dev/null +++ b/resources/project/B7BdPfT_W691i4YRWWdVQLY3Fjs/9ENWrIOXxrb6YaitWVPAXr1h5jEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IusVJs9gd_WCVNUuAddwmAvf9nAd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IusVJs9gd_WCVNUuAddwmAvf9nAd.xml new file mode 100644 index 0000000..3d41e5f --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IusVJs9gd_WCVNUuAddwmAvf9nAd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IusVJs9gd_WCVNUuAddwmAvf9nAp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IusVJs9gd_WCVNUuAddwmAvf9nAp.xml new file mode 100644 index 0000000..6a7f23f --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/IusVJs9gd_WCVNUuAddwmAvf9nAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_5Xyt_J1rG4iFP4xycYVc_CKVqUd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_5Xyt_J1rG4iFP4xycYVc_CKVqUd.xml new file mode 100644 index 0000000..d61b458 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_5Xyt_J1rG4iFP4xycYVc_CKVqUd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_5Xyt_J1rG4iFP4xycYVc_CKVqUp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_5Xyt_J1rG4iFP4xycYVc_CKVqUp.xml new file mode 100644 index 0000000..13121c5 --- /dev/null +++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/_5Xyt_J1rG4iFP4xycYVc_CKVqUp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/53njxdRRmwBSUTly-33aDJDDmCMd.xml b/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/53njxdRRmwBSUTly-33aDJDDmCMd.xml new file mode 100644 index 0000000..1c0844e --- /dev/null +++ b/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/53njxdRRmwBSUTly-33aDJDDmCMd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/53njxdRRmwBSUTly-33aDJDDmCMp.xml b/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/53njxdRRmwBSUTly-33aDJDDmCMp.xml new file mode 100644 index 0000000..add622c --- /dev/null +++ b/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/53njxdRRmwBSUTly-33aDJDDmCMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/4LjnRxn_PDoKq9Gzrp6Zt78m93wd.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/4LjnRxn_PDoKq9Gzrp6Zt78m93wd.xml index 80b5b16..d8fadf3 100644 --- a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/4LjnRxn_PDoKq9Gzrp6Zt78m93wd.xml +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/4LjnRxn_PDoKq9Gzrp6Zt78m93wd.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/5r2iUz4k_NmJ3dAxL2VFYLrl908d.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/5r2iUz4k_NmJ3dAxL2VFYLrl908d.xml index 80b5b16..d8fadf3 100644 --- a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/5r2iUz4k_NmJ3dAxL2VFYLrl908d.xml +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/5r2iUz4k_NmJ3dAxL2VFYLrl908d.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/R2I-03tuAnQmT4JLMfn6zTFlWYMd.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/R2I-03tuAnQmT4JLMfn6zTFlWYMd.xml new file mode 100644 index 0000000..d8fadf3 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/R2I-03tuAnQmT4JLMfn6zTFlWYMd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/R2I-03tuAnQmT4JLMfn6zTFlWYMp.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/R2I-03tuAnQmT4JLMfn6zTFlWYMp.xml new file mode 100644 index 0000000..7da57f1 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/R2I-03tuAnQmT4JLMfn6zTFlWYMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/RAUmIkhTqKwJybdilYHHyF0eUvgd.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/RAUmIkhTqKwJybdilYHHyF0eUvgd.xml new file mode 100644 index 0000000..d8fadf3 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/RAUmIkhTqKwJybdilYHHyF0eUvgd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/RAUmIkhTqKwJybdilYHHyF0eUvgp.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/RAUmIkhTqKwJybdilYHHyF0eUvgp.xml new file mode 100644 index 0000000..6d4720f --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/RAUmIkhTqKwJybdilYHHyF0eUvgp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/Z9CBHQoJSBnVGDcfUj4_Oi1-gewd.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/Z9CBHQoJSBnVGDcfUj4_Oi1-gewd.xml new file mode 100644 index 0000000..d8fadf3 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/Z9CBHQoJSBnVGDcfUj4_Oi1-gewd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/Z9CBHQoJSBnVGDcfUj4_Oi1-gewp.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/Z9CBHQoJSBnVGDcfUj4_Oi1-gewp.xml new file mode 100644 index 0000000..d9a77b2 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/Z9CBHQoJSBnVGDcfUj4_Oi1-gewp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/ZmntEkBOCgQjJL2XqWK7rsqMvjkd.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/ZmntEkBOCgQjJL2XqWK7rsqMvjkd.xml new file mode 100644 index 0000000..d8fadf3 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/ZmntEkBOCgQjJL2XqWK7rsqMvjkd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/ZmntEkBOCgQjJL2XqWK7rsqMvjkp.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/ZmntEkBOCgQjJL2XqWK7rsqMvjkp.xml new file mode 100644 index 0000000..8587c60 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/ZmntEkBOCgQjJL2XqWK7rsqMvjkp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/g0y7IF8VCviw4OhVvKOo7SoCShcd.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/g0y7IF8VCviw4OhVvKOo7SoCShcd.xml new file mode 100644 index 0000000..d8fadf3 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/g0y7IF8VCviw4OhVvKOo7SoCShcd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/g0y7IF8VCviw4OhVvKOo7SoCShcp.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/g0y7IF8VCviw4OhVvKOo7SoCShcp.xml new file mode 100644 index 0000000..8bf103c --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/g0y7IF8VCviw4OhVvKOo7SoCShcp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/hHtzZ7G-PvEBonVBpuAFucVCn9Yd.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/hHtzZ7G-PvEBonVBpuAFucVCn9Yd.xml index 80b5b16..d8fadf3 100644 --- a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/hHtzZ7G-PvEBonVBpuAFucVCn9Yd.xml +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/hHtzZ7G-PvEBonVBpuAFucVCn9Yd.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/prKBW6hTnJR4FlP9Y3GrjoDrI6Ud.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/prKBW6hTnJR4FlP9Y3GrjoDrI6Ud.xml new file mode 100644 index 0000000..d8fadf3 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/prKBW6hTnJR4FlP9Y3GrjoDrI6Ud.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/prKBW6hTnJR4FlP9Y3GrjoDrI6Up.xml b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/prKBW6hTnJR4FlP9Y3GrjoDrI6Up.xml new file mode 100644 index 0000000..c8b61a5 --- /dev/null +++ b/resources/project/FWb00jXzyA0ae61v0EOixwayiuU/prKBW6hTnJR4FlP9Y3GrjoDrI6Up.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/PUMahrtvKX67qazxeB1EFjsiB44d.xml b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/PUMahrtvKX67qazxeB1EFjsiB44d.xml new file mode 100644 index 0000000..80b5b16 --- /dev/null +++ b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/PUMahrtvKX67qazxeB1EFjsiB44d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/PUMahrtvKX67qazxeB1EFjsiB44p.xml b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/PUMahrtvKX67qazxeB1EFjsiB44p.xml new file mode 100644 index 0000000..576d57c --- /dev/null +++ b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/PUMahrtvKX67qazxeB1EFjsiB44p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/ulE9PYd_TlaqYfhxJlcxk9uZKPwd.xml b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/ulE9PYd_TlaqYfhxJlcxk9uZKPwd.xml new file mode 100644 index 0000000..1c0844e --- /dev/null +++ b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/ulE9PYd_TlaqYfhxJlcxk9uZKPwd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/ulE9PYd_TlaqYfhxJlcxk9uZKPwp.xml b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/ulE9PYd_TlaqYfhxJlcxk9uZKPwp.xml new file mode 100644 index 0000000..5199d61 --- /dev/null +++ b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/ulE9PYd_TlaqYfhxJlcxk9uZKPwp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/uxxJQ1_6BNIpFwr3u6UwjPxiWSEd.xml b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/uxxJQ1_6BNIpFwr3u6UwjPxiWSEd.xml new file mode 100644 index 0000000..80b5b16 --- /dev/null +++ b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/uxxJQ1_6BNIpFwr3u6UwjPxiWSEd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/uxxJQ1_6BNIpFwr3u6UwjPxiWSEp.xml b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/uxxJQ1_6BNIpFwr3u6UwjPxiWSEp.xml new file mode 100644 index 0000000..1034750 --- /dev/null +++ b/resources/project/q138eJA8Ym4eSfM3RFMVvg63QtU/uxxJQ1_6BNIpFwr3u6UwjPxiWSEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/q138eJA8Ym4eSfM3RFMVvg63QtUd.xml b/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/q138eJA8Ym4eSfM3RFMVvg63QtUd.xml new file mode 100644 index 0000000..1c0844e --- /dev/null +++ b/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/q138eJA8Ym4eSfM3RFMVvg63QtUd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/q138eJA8Ym4eSfM3RFMVvg63QtUp.xml b/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/q138eJA8Ym4eSfM3RFMVvg63QtUp.xml new file mode 100644 index 0000000..7d7f41d --- /dev/null +++ b/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/q138eJA8Ym4eSfM3RFMVvg63QtUp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/runTestSuite.m b/runTestSuite.m index 9916a79..0f01955 100644 --- a/runTestSuite.m +++ b/runTestSuite.m @@ -1,11 +1,11 @@ function [testSuite, result] = runTestSuite() % Run the test suite -% Copyright 2019-2020 The MathWorks, Inc. +% Copyright 2019-2021 The MathWorks, Inc. %% Create test suite -testSuite = matlab.unittest.TestSuite.fromPackage('wt.test'); +testSuite = matlab.unittest.TestSuite.fromProject(currentProject); %% Run tests @@ -14,4 +14,12 @@ %% Display Results ResultTable = result.table(); -disp(ResultTable); \ No newline at end of file +disp(ResultTable); + + +%% Did results all pass? +if all([result.Passed]) + disp("All Tests Passed"); +else + warning("widgets:runTestSuite","Not all tests passed. Check results."); +end \ No newline at end of file diff --git a/sandbox/ImageGallery.m b/sandbox/ImageGallery.m new file mode 100644 index 0000000..71d8962 --- /dev/null +++ b/sandbox/ImageGallery.m @@ -0,0 +1,190 @@ +classdef ImageGallery < wt.abstract.BaseWidget + % A gallery of images + + % Copyright 2020-2021 The MathWorks Inc. + + %RAJ - to do: + % verify performance with bigger files + % Do we need thumbnail generation?? + % Enable setting a folder instead of a file list + % Enable a file type filter in this case + + %% Public properties + properties (AbortSet) + + % Size of the image space in pixels + ImageSize (1,1) double = 200 + + % Image file sources + ImageSource (:,1) string = [""; ""; ""] + + end %properties + + + %% Internal Properties + properties ( Transient, NonCopyable, ... + Access = {?wt.abstract.BaseWidget, ?wt.test.BaseWidgetTest} ) + + % Image controls + Image (1,:) matlab.ui.control.Image + + % Size changed listener + SizeChangedListener event.listener + + end %properties + + + properties (AbortSet, Transient, NonCopyable, ... + Access = {?wt.abstract.BaseWidget, ?wt.test.BaseWidgetTest} ) + + % Number of visible [rows columns] + GridSize (1,2) double = [1 3] + + end %properties + + + + %% Protected methods + methods (Access = protected) + + function setup(obj) + + % Call superclass setup first to establish the grid + obj.setup@wt.abstract.BaseWidget(); + + % Set default size + obj.Position(3:4) = [400 400]; + obj.Grid.Padding = [0 0 0 0]; + + % Turn on scrollability + obj.Grid.Scrollable = true; + + % Listen to resize events + obj.SizeChangedListener = event.listener(obj,"SizeChanged",... + @(src,evt)obj.onSizeChanged(evt) ); + + end %function + + + function update(obj) + + % How many images are needed? + numImg = numel(obj.ImageSource); + oldNum = numel(obj.Image); + + % Check and update grid size as needed + obj.updateGridSize(); + + % Calculate layout position of each image + cIdx = repmat(1:obj.GridSize(2), obj.GridSize(1), 1)'; + rIdx = repmat(1:obj.GridSize(1), obj.GridSize(2), 1); + + % Add new images if needed + if numImg > oldNum + for idx = oldNum+1:numImg + obj.Image(idx) = uiimage(obj.Grid, "ScaleMethod", "fill"); + obj.Image(idx).Layout.Row = rIdx(idx); + obj.Image(idx).Layout.Column = cIdx(idx); + end + end %if + + % Update each image source + for idx = 1:numImg + wt.utility.fastSet(obj.Image(idx),... + "ImageSource", obj.ImageSource(idx)); + end %for + + % Delete any extra images + delete( obj.Image(numImg+1:end) ) + obj.Image(numImg+1:end) = []; + + end %function + + end %methods + + + + %% Private methods + methods %(Access = private) + + function onSizeChanged(obj,~) + % Triggered on size changed + + % Check and update grid size as needed + obj.updateGridSize(); + + end %function + + + function updateGridSize(obj) + % Calculate and update the grid size + + % How many images? + numImg = numel(obj.ImageSource); + + % How much space do we have? + if obj.Units == "pixels" + wAvail = obj.Position(3) + obj.Grid.ColumnSpacing; + else + pos = getpixelposition(obj.Grid); + wAvail = (pos(3) + obj.Grid.ColumnSpacing); + end + + % How many images do we need to fit? + wPerImg = obj.ImageSize + obj.Grid.ColumnSpacing; + + % How many full columns fit across? + numCol = max(1, floor(wAvail/wPerImg)); + + % How many rows do we need? (Rows can be scrolled down) + numRow = max(1, ceil(numImg/numCol)); + + % Update the layout size + obj.GridSize = [numRow numCol]; + + end %function + + + function updateLayout(obj) + % Update the layout based on the current GridSize + % This should only be called by set.GridSize + + % How many images? + numImg = min( prod(obj.GridSize), numel(obj.Image) ); + + % Make the layout updates + colWidth = repmat({obj.ImageSize}, 1, obj.GridSize(2)); + rowHeight = repmat({obj.ImageSize}, 1, obj.GridSize(1)); + + % Calculate layout position of each image + rIdx = repmat(1:obj.GridSize(1), obj.GridSize(2), 1); + cIdx = repmat(1:obj.GridSize(2), obj.GridSize(1), 1)'; + + % Update layout position of each image + for idx = 1:numImg + obj.Image(idx).Layout.Row = rIdx(idx); + obj.Image(idx).Layout.Column = cIdx(idx); + end %for + + % Update the grid layout sizes + obj.Grid.RowHeight = rowHeight; + obj.Grid.ColumnWidth = colWidth; + + end %function + + + end %methods + + + %% Accessors + methods + + function set.GridSize(obj,value) + obj.GridSize = value; + obj.updateLayout(); + end + + end % methods + +end % classdef + diff --git a/sandbox/rjImageGallery.m b/sandbox/rjImageGallery.m new file mode 100644 index 0000000..d58f5bf --- /dev/null +++ b/sandbox/rjImageGallery.m @@ -0,0 +1,20 @@ +%% Create the image gallery +f = uifigure; +g = uigridlayout(f,[1,1]); +w = ImageGallery(g,'BackgroundColor','green') + + +%% Show some images from MATLAB +searchRoot = fullfile(matlabroot,'toolbox','images','imdata'); +fileInfo = dir( fullfile(searchRoot,"*.png") ); +fileNames = sortrows( string({fileInfo.name}') ); +filePaths = fullfile(searchRoot, fileNames); +w.ImageSource = filePaths; + + +%% Big images +searchRoot = "C:\Users\rjackey\OneDrive - MathWorks\Pictures\2011-10-31 Argentina\"; +fileInfo = dir( fullfile(searchRoot,"*.jpg") ); +fileNames = sortrows( string({fileInfo.name}') ); +filePaths = fullfile(searchRoot, fileNames); +w.ImageSource = filePaths; diff --git a/widgets/+wt/+abstract/BaseWidget.m b/widgets/+wt/+abstract/BaseWidget.m index 55bce82..738a074 100644 --- a/widgets/+wt/+abstract/BaseWidget.m +++ b/widgets/+wt/+abstract/BaseWidget.m @@ -2,7 +2,7 @@ wt.mixin.ErrorHandling % Base class for a graphical widget - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Internal properties diff --git a/widgets/+wt/+apps/BaseApp.m b/widgets/+wt/+apps/BaseApp.m index 3507cde..d898a51 100644 --- a/widgets/+wt/+apps/BaseApp.m +++ b/widgets/+wt/+apps/BaseApp.m @@ -2,7 +2,7 @@ wt.mixin.ErrorHandling % Base class for Widgets Toolbox apps - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. %% Properties @@ -18,10 +18,10 @@ % Model class for App preferences %(may subclass wt.model.Preferences to add more prefs) - Preferences (1,1) wt.model.Preferences + Preferences (1,1) wt.model.Preferences % Name of group to store preferences (defaults to class name) - PreferenceGroup (1,1) string + PreferenceGroup (1,1) string end %properties @@ -39,7 +39,7 @@ end %properties - + %% Internal properties properties (Hidden, SetAccess = immutable) @@ -80,8 +80,8 @@ methods function forceUpdate(app) - % Forces update to run (For debugging only!) - + % Forces update to run (For debugging only!) + app.update(); end %function @@ -130,10 +130,11 @@ function forceUpdate(app) % Set up components app.setup_internal(); app.setup(); - app.SetupComplete = true; % Set any P-V pairs - set(app,varargin{:}); + if ~isempty(varargin) + set(app, varargin{:}); + end % Register the app with App Designer registerApp(app, app.Figure) @@ -141,15 +142,21 @@ function forceUpdate(app) % Ensure it's on screen app.moveOnScreen(); + % Mark the setup complete + app.SetupComplete = true; + + % Now, make it visible + app.Figure.Visible = 'on'; + + % Force drawing to finish + drawnow + % Update the app app.update(); % Update the title app.updateTitle(); - % Now, make it visible - app.Figure.Visible = 'on'; - end %function @@ -183,7 +190,7 @@ function close(app) end %function - + function selection = promptYesNoCancel(app, message, title, default, icon) % Prompt the user with a yes/no/cancel selection @@ -202,7 +209,7 @@ function close(app) "DefaultOption",default,... "CancelOption","Cancel",... "Icon",icon); - + end %function diff --git a/widgets/+wt/+apps/BaseSingleSessionApp.m b/widgets/+wt/+apps/BaseSingleSessionApp.m index 87defaa..d6a9211 100644 --- a/widgets/+wt/+apps/BaseSingleSessionApp.m +++ b/widgets/+wt/+apps/BaseSingleSessionApp.m @@ -1,7 +1,7 @@ classdef (Abstract) BaseSingleSessionApp < wt.apps.BaseApp % Base class for Widgets Toolbox app with a managed single session - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Properties diff --git a/widgets/+wt/+enum/AlignmentState.m b/widgets/+wt/+enum/AlignmentState.m index 26a1aba..2d5b691 100644 --- a/widgets/+wt/+enum/AlignmentState.m +++ b/widgets/+wt/+enum/AlignmentState.m @@ -2,7 +2,7 @@ %ALIGNMENTSTATE Represent alignment selections % Enumerates a list of choices - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Enumerations diff --git a/widgets/+wt/+enum/FileFolderState.m b/widgets/+wt/+enum/FileFolderState.m index b8301fd..247bf84 100644 --- a/widgets/+wt/+enum/FileFolderState.m +++ b/widgets/+wt/+enum/FileFolderState.m @@ -2,7 +2,7 @@ %FILEFOLDERSTATE Represent file/folder selections % Enumerates a list of choices - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Enumerations diff --git a/widgets/+wt/+enum/HorizontalVerticalState.m b/widgets/+wt/+enum/HorizontalVerticalState.m index fe2dabd..6d0d3ed 100644 --- a/widgets/+wt/+enum/HorizontalVerticalState.m +++ b/widgets/+wt/+enum/HorizontalVerticalState.m @@ -2,7 +2,7 @@ %HORIZONTALVERTICALSTATE Represent horizontal/vertical selections % Enumerates a list of choices - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Enumerations diff --git a/widgets/+wt/+enum/StatusState.m b/widgets/+wt/+enum/StatusState.m index 21f1151..ad231b2 100644 --- a/widgets/+wt/+enum/StatusState.m +++ b/widgets/+wt/+enum/StatusState.m @@ -2,7 +2,7 @@ %STATUSSTATE Represent status selections % Enumerates a list of choices - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Enumerations diff --git a/widgets/+wt/+eventdata/ButtonPushedData.m b/widgets/+wt/+eventdata/ButtonPushedData.m index 41f1a0c..ae9e3f1 100644 --- a/widgets/+wt/+eventdata/ButtonPushedData.m +++ b/widgets/+wt/+eventdata/ButtonPushedData.m @@ -7,7 +7,7 @@ % obj = wt.eventdata.ButtonPushedData(eventData) % - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. %% Properties properties (SetAccess = protected) @@ -41,7 +41,9 @@ end % Populate other data - obj.Text = obj.Button.Text; + if isprop(obj.Button,'Text') + obj.Text = obj.Button.Text; + end obj.Tag = obj.Button.Tag; if nargin >= 2 obj.Value = value; diff --git a/widgets/+wt/+eventdata/PropertyChangedData.m b/widgets/+wt/+eventdata/PropertyChangedData.m index 991ff55..b83d1ef 100644 --- a/widgets/+wt/+eventdata/PropertyChangedData.m +++ b/widgets/+wt/+eventdata/PropertyChangedData.m @@ -7,7 +7,7 @@ % obj = wt.eventdata.PropertyChangedData(...,'p1',v1,...) % - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. %% Properties properties (SetAccess = protected) diff --git a/widgets/+wt/+eventdata/SliderCheckboxChangedData.m b/widgets/+wt/+eventdata/SliderCheckboxChangedData.m index bdc4270..f2427e3 100644 --- a/widgets/+wt/+eventdata/SliderCheckboxChangedData.m +++ b/widgets/+wt/+eventdata/SliderCheckboxChangedData.m @@ -5,7 +5,7 @@ % obj = wt.eventdata.SliderCheckboxChangedData(name,index,prop,state,value) % - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. %% Properties properties (SetAccess = protected) diff --git a/widgets/+wt/+eventdata/ValueChangedData.m b/widgets/+wt/+eventdata/ValueChangedData.m index 23fade3..ed8d763 100644 --- a/widgets/+wt/+eventdata/ValueChangedData.m +++ b/widgets/+wt/+eventdata/ValueChangedData.m @@ -7,7 +7,7 @@ % obj = wt.eventdata.ValueChangedData(...,'p1',v1,...) % - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. %% Properties properties (SetAccess = protected) diff --git a/widgets/+wt/+mixin/BackgroundColorable.m b/widgets/+wt/+mixin/BackgroundColorable.m index 93f2830..2ac730b 100644 --- a/widgets/+wt/+mixin/BackgroundColorable.m +++ b/widgets/+wt/+mixin/BackgroundColorable.m @@ -2,7 +2,7 @@ % Mixin for component with colorable background % - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Properties @@ -48,7 +48,7 @@ function updateBackgroundColorableComponents(obj) hasProp = isprop(obj.BackgroundColorableComponents,'BackgroundColor'); - set(obj.BackgroundColorableComponents(hasProp),... + wt.utility.fastSet(obj.BackgroundColorableComponents(hasProp),... "BackgroundColor",obj.BackgroundColor); end %function diff --git a/widgets/+wt/+mixin/ButtonColorable.m b/widgets/+wt/+mixin/ButtonColorable.m index 4e697fc..86b2bca 100644 --- a/widgets/+wt/+mixin/ButtonColorable.m +++ b/widgets/+wt/+mixin/ButtonColorable.m @@ -2,7 +2,7 @@ % Mixin for component with colorable button % - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Properties @@ -47,7 +47,8 @@ function updateButtonColorableComponents(obj) - set(obj.ButtonColorableComponents,"BackgroundColor",obj.ButtonColor); + wt.utility.fastSet(obj.ButtonColorableComponents,... + "BackgroundColor",obj.ButtonColor); end %function diff --git a/widgets/+wt/+mixin/Enableable.m b/widgets/+wt/+mixin/Enableable.m index ac32a1d..aad31be 100644 --- a/widgets/+wt/+mixin/Enableable.m +++ b/widgets/+wt/+mixin/Enableable.m @@ -1,7 +1,7 @@ classdef Enableable < handle % Mixin for component with Enable property - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Properties @@ -46,7 +46,7 @@ function updateEnableableComponents(obj) - set(obj.EnableableComponents,"Enable",obj.Enable); + wt.utility.fastSet(obj.EnableableComponents,"Enable",obj.Enable); end %function diff --git a/widgets/+wt/+mixin/ErrorHandling.m b/widgets/+wt/+mixin/ErrorHandling.m index 1a9cb0f..0871b62 100644 --- a/widgets/+wt/+mixin/ErrorHandling.m +++ b/widgets/+wt/+mixin/ErrorHandling.m @@ -1,7 +1,7 @@ classdef ErrorHandling < handle %ErrorHandling Error handling methods - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. methods ( Access = protected ) diff --git a/widgets/+wt/+mixin/FieldColorable.m b/widgets/+wt/+mixin/FieldColorable.m index 71124dd..5a0fec3 100644 --- a/widgets/+wt/+mixin/FieldColorable.m +++ b/widgets/+wt/+mixin/FieldColorable.m @@ -1,7 +1,7 @@ classdef FieldColorable < handle % Mixin for component with colorable text field - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Properties properties (AbortSet) @@ -45,7 +45,7 @@ function updateFieldColorableComponents(obj) - set(obj.FieldColorableComponents,"BackgroundColor",obj.FieldColor); + wt.utility.fastSet(obj.FieldColorableComponents,"BackgroundColor",obj.FieldColor); end %function diff --git a/widgets/+wt/+mixin/FontColorable.m b/widgets/+wt/+mixin/FontColorable.m index 4245533..2f06cb8 100644 --- a/widgets/+wt/+mixin/FontColorable.m +++ b/widgets/+wt/+mixin/FontColorable.m @@ -1,7 +1,7 @@ classdef FontColorable < handle % Mixin for component with Font color (but no other editable font properties) - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Properties @@ -47,11 +47,16 @@ function updateFontColorableComponents(obj) hasFontColorProp = isprop(obj.FontColorableComponents,"FontColor"); + + wt.utility.fastSet(obj.FontColorableComponents(hasFontColorProp),... + "FontColor",obj.FontColor); + + hasForegroundColorProp = ~hasFontColorProp & ... isprop(obj.FontColorableComponents,"ForegroundColor"); - set(obj.FontColorableComponents(hasFontColorProp),"FontColor",obj.FontColor); - set(obj.FontColorableComponents(hasForegroundColorProp),"ForegroundColor",obj.FontColor); + wt.utility.fastSet(obj.FontColorableComponents(hasForegroundColorProp),... + "ForegroundColor",obj.FontColor); end %function diff --git a/widgets/+wt/+mixin/FontStyled.m b/widgets/+wt/+mixin/FontStyled.m index fb2306a..554876f 100644 --- a/widgets/+wt/+mixin/FontStyled.m +++ b/widgets/+wt/+mixin/FontStyled.m @@ -1,7 +1,7 @@ classdef FontStyled < handle % Mixin for component with Font properties - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Properties @@ -81,7 +81,7 @@ function updateFontStyledComponents(obj,prop,value) if nargin > 1 % Update specific property hasProp = isprop(obj.FontStyledComponents,prop); - set(obj.FontStyledComponents(hasProp),prop,value); + wt.utility.fastSet(obj.FontStyledComponents(hasProp),prop,value); else % Update all obj.updateFontStyledComponents("FontName",obj.FontName) diff --git a/widgets/+wt/+mixin/TitleColorable.m b/widgets/+wt/+mixin/TitleColorable.m index b2bdcc7..c1db0bd 100644 --- a/widgets/+wt/+mixin/TitleColorable.m +++ b/widgets/+wt/+mixin/TitleColorable.m @@ -1,7 +1,7 @@ classdef TitleColorable < handle % Mixin for component with Title font color - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Properties @@ -46,7 +46,7 @@ function updateTitleColorableComponents(obj) - set(obj.TitleColorableComponents,"FontColor",obj.TitleColor) + wt.utility.fastSet(obj.TitleColorableComponents,"FontColor",obj.TitleColor) end %function diff --git a/widgets/+wt/+mixin/Tooltipable.m b/widgets/+wt/+mixin/Tooltipable.m index 09b9c4c..6013394 100644 --- a/widgets/+wt/+mixin/Tooltipable.m +++ b/widgets/+wt/+mixin/Tooltipable.m @@ -1,7 +1,7 @@ classdef Tooltipable < handle % Mixin for component with Tooltip property - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Properties @@ -46,7 +46,7 @@ function updateTooltipableComponents(obj) - set(obj.TooltipableComponents,"Tooltip",obj.Tooltip); + wt.utility.fastSet(obj.TooltipableComponents,"Tooltip",obj.Tooltip); end %function diff --git a/widgets/+wt/+model/BaseModel.m b/widgets/+wt/+model/BaseModel.m index e14995f..0d1eb46 100644 --- a/widgets/+wt/+model/BaseModel.m +++ b/widgets/+wt/+model/BaseModel.m @@ -8,7 +8,7 @@ % to enable apps/widgets to listen to model changes % - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. diff --git a/widgets/+wt/+model/BaseSession.m b/widgets/+wt/+model/BaseSession.m index b7904e4..c87f80e 100644 --- a/widgets/+wt/+model/BaseSession.m +++ b/widgets/+wt/+model/BaseSession.m @@ -6,7 +6,7 @@ % trigger a public event "PropertyChanged" when value is set. The app % will listen for these changes. - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. %% Properties diff --git a/widgets/+wt/+model/Preferences.m b/widgets/+wt/+model/Preferences.m index 0fef88f..b4ec8db 100644 --- a/widgets/+wt/+model/Preferences.m +++ b/widgets/+wt/+model/Preferences.m @@ -5,7 +5,7 @@ % The preferences may be set to automatically load upon creating an % instance, and save upon destruction of an instance of this class. - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. diff --git a/widgets/+wt/+test/BaseWidgetTest.m b/widgets/+wt/+test/BaseWidgetTest.m index 1fec2af..fbd09e3 100644 --- a/widgets/+wt/+test/BaseWidgetTest.m +++ b/widgets/+wt/+test/BaseWidgetTest.m @@ -1,7 +1,7 @@ classdef BaseWidgetTest < matlab.uitest.TestCase % Implements a unit test for a widget or component - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. %% Properties @@ -12,19 +12,33 @@ end + %% Properties + properties (SetAccess = protected) + + % Test can trigger this when ButtonPressedFcn is fired by toolbar + CallbackCount (1,1) double {mustBeInteger, mustBeNonnegative} = 0 + + % Eventdata of button pushes + CallbackEvents (:,1) %wt.eventdata.ButtonPushedData + + end %properties + + %% Class Setup methods (TestClassSetup) function createFigure(testCase) - testCase.Figure = uifigure('Position',[100 100 300 400]); + testCase.Figure = uifigure('Position',[100 100 600 600]); + testCase.Figure.Name = "Unit Test - " + class(testCase); % Set up a grid layout - numRows = 12; - rowHeight = 25; + numRows = 10; + rowHeight = 50; testCase.Grid = uigridlayout(testCase.Figure,[numRows,1]); testCase.Grid.Scrollable = true; testCase.Grid.RowHeight = repmat({rowHeight},1,numRows); + testCase.Grid.BackgroundColor = [0 .8 0]; end %function @@ -43,9 +57,37 @@ function removeFigure(testCase) end %methods + + %% Test Method Setup + methods (TestMethodSetup) + + function setup(testCase) + + % Reset callback counter for each test + testCase.CallbackCount = 0; + testCase.CallbackEvents(:) = []; + + end %function + + end %methods + + %% Helper Methods methods (Access = protected) + function onCallbackTriggered(testCase, evt) + % Callback when a button is pressed + + testCase.CallbackCount = testCase.CallbackCount + 1; + if testCase.CallbackCount > 1 + testCase.CallbackEvents(end+1) = evt; + else + testCase.CallbackEvents = evt; + end + + end %function + + function verifySetProperty(testCase,propName,newValue,expValue) % If no expected new value after set was given, assume it @@ -115,6 +157,18 @@ function verifyEquality(testCase, actualValue, expValue) end %function + function verifyMethod(testCase, fcn, varargin) + % Verify a method call on the widget + + if ~isa(fcn,'function_handle') + fcn = str2func(fcn); + end + fcn_send = @()fcn(testCase.Widget, varargin{:}); + testCase.verifyWarningFree(fcn_send); + + end %function + + end %methods end %classdef \ No newline at end of file diff --git a/widgets/+wt/+test/ButtonGrid.m b/widgets/+wt/+test/ButtonGrid.m new file mode 100644 index 0000000..0bbbe7d --- /dev/null +++ b/widgets/+wt/+test/ButtonGrid.m @@ -0,0 +1,196 @@ +classdef ButtonGrid < wt.test.BaseWidgetTest + % Implements a unit test for a widget or component + + % Copyright 2020-2021 The MathWorks, Inc. + + + %% Test Method Setup + methods (TestMethodSetup) + + function setup(testCase) + + % Call superclass method + testCase.setup@wt.test.BaseWidgetTest(); + + fcn = @()wt.ButtonGrid(testCase.Grid); + testCase.Widget = verifyWarningFree(testCase,fcn); + + % Set callback + testCase.Widget.ButtonPushedFcn = @(s,e)onCallbackTriggered(testCase,e); + + % Ensure it renders + drawnow + + end %function + + end %methods + + + %% Unit Tests + methods (Test) + + function testCreationByIcon(testCase) + % Add buttons by Icon only + + icon = ["add" "delete" "play" "pause" "stop"] + "_24.png"; + + testCase.verifySetProperty("Icon", icon); + + end %function + + + function testCreationByTextAndIcon(testCase) + % Add buttons by Icon and Text + + text = ["Add" "Delete" "Play" "Pause" "Stop"]; + icon = ["add" "delete" "play" "pause" "stop"] + "_24.png"; + + testCase.verifySetProperty("Icon", icon); + testCase.verifySetProperty("Text", text); + + expNum = max(numel(text), numel(icon)); + testCase.verifyNumElements(testCase.Widget.Button, expNum) + + end %function + + + function testMissingIcons(testCase) + % Some buttons don't have icon + + text = ["Add" "Delete" "Play" "Pause" "Stop"]; + icon = ["add" "delete" "play" "pause"] + "_24.png"; + + testCase.verifySetProperty("Icon", icon); + testCase.verifySetProperty("Text", text); + + expNum = max(numel(text), numel(icon)); + testCase.verifyNumElements(testCase.Widget.Button, expNum) + + end %function + + + function testMissingText(testCase) + % Some buttons don't have text + + text = ["Add" "" "Play" "Pause"]; + icon = ["add" "delete" "play" "pause" "stop"] + "_24.png"; + + testCase.verifySetProperty("Icon", icon); + testCase.verifySetProperty("Text", text); + + expNum = max(numel(text), numel(icon)); + testCase.verifyNumElements(testCase.Widget.Button, expNum) + + end %function + + + function testExtraTooltip(testCase) + % Extra tooltips provided don't produce more buttons + + text = ["Add" "" "Play" "Pause"]; + icon = ["add" "delete" "play" "pause"] + "_24.png"; + tip = "Press to " + ["add" "delete" "play" "pause" "stop"]; + + testCase.verifySetProperty("Icon", icon); + testCase.verifySetProperty("Text", text); + testCase.verifySetProperty("Tooltip", tip); + + expNum = max(numel(text), numel(icon)); + testCase.verifyNumElements(testCase.Widget.Button, expNum) + + end %function + + + function testPressButton(testCase) + % Extra tooltips provided don't produce more buttons + + text = ["Add" "Delete" "Play"]; + icon = ["add" "delete" "play"] + "_24.png"; + tag = text + "_tag"; + + testCase.verifySetProperty("Icon", icon); + testCase.verifySetProperty("Text", text); + testCase.verifySetProperty("ButtonTag", tag); + + buttons = testCase.Widget.Button; + + numButtons = numel(text); + for idx = 1:numButtons + testCase.press(buttons(idx)); + end + + % Each button should have triggered one callback + testCase.verifyEqual(testCase.CallbackCount, numButtons) + + % Button eventdata should match the pushes in order + evts = testCase.CallbackEvents; + testCase.verifyEqual([evts.Button], buttons) + testCase.verifyEqual(string({evts.Text}), text) + testCase.verifyEqual(string({evts.Tag}), tag) + + end %function + + + function testBackgroundColor(testCase) + + % Set the backgroundcolor + newValue = [1 0.5 0.2]; + testCase.verifySetProperty("BackgroundColor", newValue); + testCase.verifyEqual(testCase.Widget.Grid.BackgroundColor, newValue); + + end %function + + + function testOrientation(testCase) + + icon = ["up" "down"] + "_24.png"; + testCase.verifySetProperty("Icon", icon); + + % Verify the orientation default + actVal = string(testCase.Widget.Orientation); + testCase.verifyMatches(actVal, "horizontal"); + testCase.verifyNumElements(testCase.Widget.Grid.RowHeight, 1); + testCase.verifyNumElements(testCase.Widget.Grid.ColumnWidth, numel(icon)); + + % Set the orientation + testCase.verifySetProperty("Orientation", "vertical"); + testCase.verifyNumElements(testCase.Widget.Grid.RowHeight, numel(icon)); + testCase.verifyNumElements(testCase.Widget.Grid.ColumnWidth, 1); + + end %function + + + function testIconAlignment(testCase) + + icon = "up_24.png"; + text = "Up"; + testCase.verifySetProperty("Icon", icon); + testCase.verifySetProperty("Text", text); + + button = testCase.Widget.Button; + + % Set the alignment + newValue = "top"; + testCase.verifySetProperty("IconAlignment", newValue); + testCase.verifyMatches(button.IconAlignment, newValue); + + % Set the alignment + newValue = "bottom"; + testCase.verifySetProperty("IconAlignment", newValue); + testCase.verifyMatches(button.IconAlignment, newValue); + + % Set the alignment + newValue = "right"; + testCase.verifySetProperty("IconAlignment", newValue); + testCase.verifyMatches(button.IconAlignment, newValue); + + % Set the alignment + newValue = "left"; + testCase.verifySetProperty("IconAlignment", newValue); + testCase.verifyMatches(button.IconAlignment, newValue); + + end %function + + end %methods (Test) + +end %classdef \ No newline at end of file diff --git a/widgets/+wt/+test/CheckboxList.m b/widgets/+wt/+test/CheckboxList.m new file mode 100644 index 0000000..2050324 --- /dev/null +++ b/widgets/+wt/+test/CheckboxList.m @@ -0,0 +1,141 @@ +classdef CheckboxList < wt.test.BaseWidgetTest + % Implements a unit test for a widget or component + + % Copyright 2020-2021 The MathWorks, Inc. + + + + %% Class Setup + methods (TestClassSetup) + + function createFigure(testCase) + + % Call superclass method + testCase.createFigure@wt.test.BaseWidgetTest(); + + % Make the figure taller + testCase.Figure.Position(4) = 600; + + % Modify the grid row height + testCase.Grid.RowHeight = {'1x','1x','1x'}; + testCase.Grid.ColumnWidth = {'1x','1x','1x'}; + + end %function + + end %methods + + + %% Test Method Setup + methods (TestMethodSetup) + + function setup(testCase) + + fcn = @()wt.CheckboxList(testCase.Grid); + testCase.Widget = verifyWarningFree(testCase,fcn); + + % Set callback + testCase.Widget.ValueChangedFcn = @(s,e)onCallbackTriggered(testCase,e); + + % Ensure it renders + drawnow + + end %function + + end %methods + + + %% Unit Tests + methods (Test) + + function testItems(testCase) + + % Set the items + items = [ + "California" + "Massachusetts" + "New Mexico" + ]; + testCase.verifySetProperty("Items", items); + + % Verify value adapts + numItems = numel(items); + actVal = testCase.Widget.Value; + expVal = true(numItems, 1); + testCase.verifyEqual(actVal, expVal); + + % Add more items + items = "Item " + string(1:10)'; + testCase.verifySetProperty("Items", items); + + % Verify the value adapts + numItems = numel(items); + actVal = testCase.Widget.Value; + expVal = true(numItems, 1); + testCase.verifyEqual(actVal, expVal); + + end %function + + + function testValue(testCase) + + % Verify value is matching size + numItems = numel(testCase.Widget.Items); + actVal = testCase.Widget.Value; + expVal = true(numItems, 1); + testCase.verifyEqual(actVal, expVal); + + % Get the checkbox controls + cbox = testCase.Widget.ItemCheck; + + % Change all values to false + newValue = false(numItems, 1); + testCase.verifySetProperty("Value", newValue) + testCase.verifyEqual([cbox.Value]', newValue); + + % Toggle checkboxes + idxOn = 3; + testCase.choose(cbox(idxOn)); + newValue(idxOn) = true; + testCase.verifyEqual(testCase.Widget.Value, newValue) + testCase.verifyEqual([cbox.Value]', newValue); + + % Verify callback fired + testCase.verifyEqual(testCase.CallbackCount, 1); + + end %function + + + function testSelectAll(testCase) + + numItems = numel(testCase.Widget.Items); + allFalse = false(numItems, 1); + allTrue = true(numItems, 1); + + % Turn off all checkbox values + testCase.verifySetProperty("Value", allFalse); + + % Toggle on select all + testCase.verifySetProperty("ShowSelectAll", true); + + % Re-verify value + testCase.verifyEqual(testCase.Widget.Value, allFalse); + + % Get the checkbox controls + cbox = testCase.Widget.ItemCheck; + AllCheck = testCase.Widget.AllCheck; + + % Choose select all + testCase.choose(AllCheck); + + % Verify new value + testCase.verifyEqual([cbox.Value]', allTrue); + testCase.verifyEqual(testCase.Widget.Value, allTrue); + + % Verify callback fired + testCase.verifyEqual(testCase.CallbackCount, 1); + + end %function + + end %methods (Test) + +end %classdef \ No newline at end of file diff --git a/widgets/+wt/+test/ColorSelector.m b/widgets/+wt/+test/ColorSelector.m index 13412aa..fa21a7a 100644 --- a/widgets/+wt/+test/ColorSelector.m +++ b/widgets/+wt/+test/ColorSelector.m @@ -1,7 +1,7 @@ classdef ColorSelector < wt.test.BaseWidgetTest % Implements a unit test for a widget or component - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. diff --git a/widgets/+wt/+test/FileSelector.m b/widgets/+wt/+test/FileSelector.m index ba35911..fb29f11 100644 --- a/widgets/+wt/+test/FileSelector.m +++ b/widgets/+wt/+test/FileSelector.m @@ -1,7 +1,7 @@ classdef FileSelector < wt.test.BaseWidgetTest % Implements a unit test for a widget or component - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. diff --git a/widgets/+wt/+test/ListSelector.m b/widgets/+wt/+test/ListSelector.m index 78c4335..2611da9 100644 --- a/widgets/+wt/+test/ListSelector.m +++ b/widgets/+wt/+test/ListSelector.m @@ -1,7 +1,7 @@ classdef ListSelector < wt.test.BaseWidgetTest % Implements a unit test for a widget or component - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. %% Properties properties diff --git a/widgets/+wt/+test/ListSelectorTwoPane.m b/widgets/+wt/+test/ListSelectorTwoPane.m index 5f698c0..ab249cd 100644 --- a/widgets/+wt/+test/ListSelectorTwoPane.m +++ b/widgets/+wt/+test/ListSelectorTwoPane.m @@ -4,7 +4,7 @@ % This test for ListSelectorTwoPane reuses those of ListSelector % with a few additions and modifications - % Copyright 2020 The MathWorks, Inc. + % Copyright 2020-2021 The MathWorks, Inc. %% Class Setup diff --git a/widgets/+wt/+test/PasswordField.m b/widgets/+wt/+test/PasswordField.m new file mode 100644 index 0000000..a15e08c --- /dev/null +++ b/widgets/+wt/+test/PasswordField.m @@ -0,0 +1,66 @@ +classdef PasswordField < wt.test.BaseWidgetTest + % Implements a unit test for a widget or component + + % Copyright 2020-2021 The MathWorks, Inc. + + + %% Test Method Setup + methods (TestMethodSetup) + + function setup(testCase) + + fcn = @()wt.PasswordField(testCase.Grid); + testCase.Widget = verifyWarningFree(testCase,fcn); + + % Set callback + testCase.Widget.ValueChangedFcn = @(s,e)onCallbackTriggered(testCase,e); + + % Ensure it renders + drawnow + + end %function + + end %methods + + + %% Unit Tests + methods (Test) + + function testValue(testCase) + + % Get the password field + passField = testCase.Widget.PasswordControl; + + % Change the value programmatically + newValue = "AbC435!"; + testCase.verifySetProperty("Value", newValue); + testCase.verifyMatches(passField.Data, newValue); + + + %testCase.verifyTypeAction(passField, newValue, "Value"); + % Verify callback triggered + %testCase.verifyEqual(testCase.CallbackCount, 1) + + end %function + + + %RAJ - Unfortunately, can't type in a uihtml + + % function testTyping(testCase) + % + % % Get the password field + % passField = testCase.Widget.PasswordControl; + % + % % Type a new value + % newValue = "PasswordABC123"; + % testCase.verifyTypeAction(passField, newValue, "Value"); + % testCase.verifyMatches(passField.Data, newValue); + % + % % Verify callback triggered + % testCase.verifyEqual(testCase.CallbackCount, 1) + % + % end %function + + end %methods (Test) + +end %classdef \ No newline at end of file diff --git a/widgets/+wt/+test/ProgressBar.m b/widgets/+wt/+test/ProgressBar.m new file mode 100644 index 0000000..c1c3235 --- /dev/null +++ b/widgets/+wt/+test/ProgressBar.m @@ -0,0 +1,221 @@ +classdef ProgressBar < wt.test.BaseWidgetTest + % Implements a unit test for a widget or component + + % Copyright 2020-2021 The MathWorks, Inc. + + + %% Test Method Setup + methods (TestMethodSetup) + + function setup(testCase) + + fcn = @()wt.ProgressBar(testCase.Grid); + testCase.Widget = verifyWarningFree(testCase,fcn); + + % Set callback + testCase.Widget.CancelPressedFcn = @(s,e)onCallbackTriggered(testCase,e); + + % Ensure it renders + drawnow + + end %function + + end %methods + + + %% Unit Tests + methods (Test) + + function testProgress(testCase) + % Test the progress steps + + % Get the controls + statusLabel = testCase.Widget.StatusTextLabel; + timeLabel = testCase.Widget.RemTimeLabel; + + % Verify starting bar is empty + pos = testCase.Widget.ProgressPanel.Position; + testCase.assumeEqual(testCase.Widget.ProgressPanel.Units, 'pixels') + testCase.verifyEqual(pos(3), 0) + + % Start + testCase.verifyMethod("startProgress") + + % Start with message + message = "Now started"; + testCase.verifyMethod("startProgress", message) + + % Verify text and time + testCase.verifyMatches(statusLabel.Text, message) + testCase.verifyEqual(testCase.Widget.RemainingTime, seconds(inf)) + + + % Update 1 + pause(2) + value = 0.3; + message = ""; + testCase.verifyMethod(@setProgress, value) + + % Verify bar size + pause(0.5) %needs to resize, drawnow doesn't work! + testCase.assumeEqual(testCase.Widget.Units, 'pixels') + wPos = testCase.Widget.Position; + pos = testCase.Widget.ProgressPanel.Position; + testCase.verifyGreaterThan(pos(3), wPos(3)/10) + testCase.verifyLessThan(pos(3), wPos(3)/2) + + % Verify text and time + actVal = string(strtrim(statusLabel.Text)); + testCase.verifyEqual(actVal, message) + remTime = testCase.Widget.RemainingTime; + testCase.verifyGreaterThanOrEqual(remTime, seconds(3)) + + + % Update 2 with message + pause(2) + value = 0.6; + message = "60% Complete"; + testCase.verifyMethod(@setProgress, value, message) + + % Verify bar size + pause(0.5) %needs to resize, drawnow doesn't work! + pos = testCase.Widget.ProgressPanel.Position; + testCase.verifyGreaterThan(pos(3), wPos(3)/2) + testCase.verifyLessThan(pos(3), wPos(3)) + + % Verify text and time + actVal = string(strtrim(statusLabel.Text)); + testCase.verifyEqual(actVal, message) + remTime = testCase.Widget.RemainingTime; + testCase.verifyGreaterThanOrEqual(remTime, seconds(1)) + + % Verify the displayed time text also + remTime = duration(timeLabel.Text,'InputFormat','mm:ss'); + testCase.verifyGreaterThanOrEqual(remTime, seconds(1)) + + + % Finish + message = ""; + testCase.verifyMethod("finishProgress") + + % Verify bar size + pause(0.5) %needs to resize, drawnow doesn't work! + pos = testCase.Widget.ProgressPanel.Position; + testCase.verifyEqual(pos(3), 0) + + % Verify text and time + actVal = string(strtrim(statusLabel.Text)); + testCase.verifyEqual(actVal, message) + remTime = testCase.Widget.RemainingTime; + testCase.verifyEqual(remTime, duration(missing)) + + % Verify the displayed time text also + actVal = string(strtrim(timeLabel.Text)); + testCase.verifyEqual(actVal, ""); + + + % Finish with message + message = "Now complete"; + testCase.verifyMethod("finishProgress", message) + + % Verify text and time + actVal = string(strtrim(statusLabel.Text)); + testCase.verifyEqual(actVal, message) + remTime = testCase.Widget.RemainingTime; + testCase.verifyEqual(remTime, duration(missing)) + + end %function + + + function testIndeterminate(testCase) + + % Get the displayed image + indBar = testCase.Widget.IndeterminateBar; + + % Not visibile by default + testCase.verifyFalse(indBar.Visible); + + % Turn it on + testCase.verifySetProperty("Indeterminate", true); + + % Still not visible until started + testCase.verifyFalse(indBar.Visible); + + % Start + testCase.verifyMethod("startProgress") + + % Finally visible + testCase.verifyTrue(indBar.Visible); + + end %function + + + function testShowCancel(testCase) + + % Get the cancel button + cancelButton = testCase.Widget.CancelButton; + + % Not visibile by default + testCase.verifyFalse(cancelButton.Visible); + + % Enable cancel button + testCase.verifySetProperty("ShowCancel", true); + + % Still not visible until started + testCase.verifyFalse(cancelButton.Visible); + + % No cancel detected yet + testCase.verifyFalse(testCase.Widget.CancelRequested) + + % Start + testCase.verifyMethod("startProgress") + + % Finally visible + testCase.verifyTrue(cancelButton.Visible); + + % Advance progress + pause(1.5) + testCase.verifyMethod(@setProgress, 0.5); + + % No cancel detected yet + testCase.verifyFalse(testCase.Widget.CancelRequested) + + % Press the button + testCase.press(cancelButton); + + % Verify the cancel operation + testCase.verifyTrue(testCase.Widget.CancelRequested) + testCase.verifyGreaterThan(testCase.CallbackCount, 0) + + end %function + + + function testShowTimeRemaining(testCase) + + % Get the controls + timeLabel = testCase.Widget.RemTimeLabel; + + % Start + testCase.verifyMethod("startProgress") + + % Update 1 + pause(2) + value = 0.3; + testCase.verifyMethod(@setProgress, value) + + % Verify the displayed time text + remTime = duration(timeLabel.Text,'InputFormat','mm:ss'); + testCase.verifyGreaterThanOrEqual(remTime, seconds(1)) + + % Disable the time display + testCase.verifySetProperty("ShowTimeRemaining", false); + + % Verify the displayed time text + timeText = strtrim(char(timeLabel.Text)); + testCase.verifyEmpty(timeText) + + end %function + + end %methods (Test) + +end %classdef \ No newline at end of file diff --git a/widgets/+wt/+test/SliderSpinner.m b/widgets/+wt/+test/SliderSpinner.m index d02fb27..b50b7ba 100644 --- a/widgets/+wt/+test/SliderSpinner.m +++ b/widgets/+wt/+test/SliderSpinner.m @@ -1,26 +1,8 @@ classdef SliderSpinner < wt.test.BaseWidgetTest % Implements a unit test for a widget or component - % Copyright 2020 The MathWorks, Inc. - - - %% Class Setup - methods (TestClassSetup) - - function createFigure(testCase) - - % Call superclass method - testCase.createFigure@wt.test.BaseWidgetTest(); - - % Modify the grid row height - numRows = 8; - rowHeight = 40; - testCase.Grid.RowHeight = repmat({rowHeight},1,numRows); - - end %function - - end %methods - + % Copyright 2020-2021 The MathWorks, Inc. + %% Test Method Setup methods (TestMethodSetup) @@ -29,6 +11,11 @@ function setup(testCase) fcn = @()wt.SliderSpinner(testCase.Grid); testCase.Widget = verifyWarningFree(testCase,fcn); + + % Set callback + testCase.Widget.ValueChangedFcn = @(s,e)onCallbackTriggered(testCase,e); + + % Ensure it renders drawnow end %function @@ -126,6 +113,9 @@ function testSpinnerEdit(testCase) expValue = 1; testCase.verifyTypeAction(spinnerControl, newValue, "Value", expValue); + % Verify callback triggered + testCase.verifyEqual(testCase.CallbackCount, 1) + % Type an invalid value newValue = 398; expValue = 1; @@ -134,6 +124,9 @@ function testSpinnerEdit(testCase) % If continuing in this test, need a pause(1) to wait for error % flag to disappear! + % Verify callback did not trigger on invalid! + testCase.verifyEqual(testCase.CallbackCount, 1) + end %function @@ -148,6 +141,10 @@ function testSpinnerButtons(testCase) testCase.verifyControlValues(expValue); testCase.verifyEqual(testCase.Widget.Value, expValue); + % Verify callback triggered + testCase.verifyEqual(testCase.CallbackCount, 1) + + % Click the spinner buttons expValue = 0; testCase.press(spinnerControl,'down'); @@ -155,6 +152,7 @@ function testSpinnerButtons(testCase) testCase.verifyControlValues(expValue); testCase.verifyEqual(testCase.Widget.Value, expValue); + % Test the boundaries expValue = 0; testCase.press(spinnerControl,'down'); @@ -188,6 +186,10 @@ function testSlider(testCase) testCase.verifyControlValues(expValue); testCase.verifyEqual(testCase.Widget.Value, expValue); + % Verify callback triggered + testCase.verifyEqual(testCase.CallbackCount, 1) + + % Drag the slider newValue = 32; expValue = 32; diff --git a/widgets/+wt/+test/TaskStatusTable.m b/widgets/+wt/+test/TaskStatusTable.m new file mode 100644 index 0000000..33314ad --- /dev/null +++ b/widgets/+wt/+test/TaskStatusTable.m @@ -0,0 +1,167 @@ +classdef TaskStatusTable < wt.test.BaseWidgetTest + % Implements a unit test for a widget or component + + % Copyright 2020-2021 The MathWorks, Inc. + + + + %% Class Setup + methods (TestClassSetup) + + function createFigure(testCase) + + % Call superclass method + testCase.createFigure@wt.test.BaseWidgetTest(); + + % Modify the grid row height + testCase.Grid.RowHeight = {'1x','1x'}; + testCase.Grid.ColumnWidth = {'1x','1x','1x'}; + + end %function + + end %methods + + + %% Test Method Setup + methods (TestMethodSetup) + + function setup(testCase) + + fcn = @()wt.TaskStatusTable(testCase.Grid); + testCase.Widget = verifyWarningFree(testCase,fcn); + + % Set callback + testCase.Widget.ButtonPushedFcn = @(s,e)onCallbackTriggered(testCase,e); + + % Ensure it renders + drawnow + + end %function + + end %methods + + + %% Unit Tests + methods (Test) + + function testItems(testCase) + + % Set the statuses to complete + numItems = numel(testCase.Widget.Items); + newStatus = repmat(wt.enum.StatusState.complete, numItems, 1); + testCase.verifySetProperty("Status", newStatus); + + % Set the items + items = [ + "California" + "Massachusetts" + "New Mexico" + ]; + testCase.verifySetProperty("Items", items); + + % Verify status adapts to new items size + numItems = numel(items); + actVal = string(testCase.Widget.Status); + expVal = repmat("complete", numItems, 1); + testCase.verifyEqual(actVal, expVal); + + % Add more items + items(end+1) = "Arizona"; + testCase.verifySetProperty("Items", items); + + % Verify the value adapts + numItems = numel(items); + actVal = string(testCase.Widget.Status); + expVal = repmat("complete", numItems, 1); + testCase.verifyEqual(actVal, expVal); + + end %function + + + function testButtonsAndSelection(testCase) + + % Verify selection color + selColor = [0.3 0.9 1]; + testCase.verifySetProperty("SelectionColor", selColor); + + % Get the buttons + backButton = testCase.Widget.BackButton; + fwdButton = testCase.Widget.ForwardButton; + + % First item selected by default + testCase.verifyEqual(testCase.Widget.SelectedIndex, 1) + + % Can't press back + testCase.verifyFalse(backButton.Enable) + testCase.press(backButton) + testCase.verifyEqual(testCase.Widget.SelectedIndex, 1) + + % Push forward + testCase.verifyTrue(fwdButton.Enable) + testCase.press(fwdButton) + testCase.verifyEqual(testCase.Widget.SelectedIndex, 2) + + % Push forward to end + numItems = numel(testCase.Widget.Items); + testCase.verifyTrue(fwdButton.Enable) + for idx = 1:numItems-1 + testCase.press(fwdButton) + end + testCase.verifyTrue(backButton.Enable) + testCase.verifyFalse(fwdButton.Enable) + testCase.verifyEqual(testCase.Widget.SelectedIndex, numItems) + + % Now, programmatically change the index + newIdx = 3; + testCase.verifySetProperty("SelectedIndex", newIdx); + testCase.verifyTrue(backButton.Enable) + testCase.verifyTrue(fwdButton.Enable) + testCase.verifyEqual(testCase.Widget.SelectedIndex, newIdx) + + % Verify selection color of selected label + actValue = testCase.Widget.Label(newIdx).BackgroundColor; + testCase.verifyEqual(actValue, selColor) + + end %function + + + function testButtonRow(testCase) + + % Get the buttons + backButton = testCase.Widget.BackButton; + fwdButton = testCase.Widget.ForwardButton; + + % Set the selected index + testCase.verifySetProperty("SelectedIndex", 2); + testCase.verifyTrue(backButton.Enable) + testCase.verifyTrue(fwdButton.Enable) + + % Test programmatic disable - forward button + testCase.verifySetProperty("EnableForward", false); + testCase.verifyTrue(backButton.Enable) + testCase.verifyFalse(fwdButton.Enable) + + % Test programmatic disable - back button + testCase.verifySetProperty("EnableBack", false); + testCase.verifyFalse(backButton.Enable) + testCase.verifyFalse(fwdButton.Enable) + + % Test hide button row + testCase.verifyGreaterThan(testCase.Widget.Grid.RowHeight{2}, 0) + testCase.verifySetProperty("ShowButtonRow", false); + testCase.verifyEqual(testCase.Widget.Grid.RowHeight{2}, 0) + + end %function + + + function testStatusMessage(testCase) + + newValue = "Test Message"; + testCase.verifySetProperty("StatusMessage", newValue); + testCase.verifyMatches(testCase.Widget.StatusLabel.Text, newValue) + + end %function + + end %methods (Test) + +end %classdef \ No newline at end of file diff --git a/widgets/+wt/+test/Toolbar.m b/widgets/+wt/+test/Toolbar.m new file mode 100644 index 0000000..b0b94ab --- /dev/null +++ b/widgets/+wt/+test/Toolbar.m @@ -0,0 +1,383 @@ +classdef Toolbar < wt.test.BaseWidgetTest + % Implements a unit test for a widget or component + + % Copyright 2021 The MathWorks, Inc. + + + %% Properties + properties (SetAccess = protected) + + % Test can trigger this when ButtonPressedFcn is fired by toolbar + ButtonPushCallbackDone (1,1) logical = false; + + end %properties + + + %% Class Setup + methods (TestClassSetup) + + function createFigure(testCase) + + % Call superclass method + testCase.createFigure@wt.test.BaseWidgetTest(); + + % Make the figure wider + testCase.Figure.Position([3 4]) = [600 600]; + + % Modify the grid row height + numRows = 6; + rowHeight = 90; + testCase.Grid.RowHeight = repmat({rowHeight},1,numRows); + + end %function + + end %methods + + + %% Test Method Setup + methods (TestMethodSetup) + + function setup(testCase) + + fcn = @()wt.Toolbar(testCase.Grid); + testCase.Widget = verifyWarningFree(testCase,fcn); + + % Set default callback + testCase.Widget.ButtonPushedFcn = @(s,e)onButtonPushed(testCase); + + % Default state is false when each test begins + testCase.ButtonPushCallbackDone = false; + + % Ensure it renders + drawnow + + end %function + + end %methods + + + %% Helper methods + methods (Access = private) + + function onButtonPushed(testCase, ~) + % Callback when a button is pressed + + testCase.ButtonPushCallbackDone = true; + + end %function + + + function button = verifyAddButton(testCase, section, name, icon) + % Adds a button to the last horizontal section (or to the + % specified section) + + arguments + testCase + section (1,1) wt.toolbar.BaseSection + name (1,1) string = "" + icon (1,1) string = "" + end + + fcn = @()addButton(section, icon, name); + button = testCase.verifyWarningFree(fcn); + + % Verify the buttons changed + newButton = section.Component(end); + testCase.verifyClass(newButton, "matlab.ui.control.Button") + testCase.verifyMatches(name, newButton.Text) + testCase.verifyMatches(icon, newButton.Icon) + + end %function + + + function button = verifyAddStateButton(testCase, section, name, icon) + % Adds a button to the last horizontal section (or to the + % specified section) + + arguments + testCase + section (1,1) wt.toolbar.BaseSection + name (1,1) string = "" + icon (1,1) string = "" + end + + fcn = @()addStateButton(section, icon, name); + button = testCase.verifyWarningFree(fcn); + + % Verify the buttons changed + newButton = section.Component(end); + testCase.verifyClass(newButton, "matlab.ui.control.StateButton") + testCase.verifyMatches(name, newButton.Text) + testCase.verifyMatches(icon, newButton.Icon) + + end %function + + + function section = verifyAddVerticalSection(testCase, section) + % Adds a vertical section to the last horizontal section (or to + % the specified section) + + arguments + testCase + section (1,1) wt.toolbar.BaseSection + end + + fcn = @()addVerticalSection(section); + section = testCase.verifyWarningFree(fcn); + + end %function + + + function section = addMultiSections(testCase, sizes) + + % Preallocate + section(numel(sizes),1) = wt.toolbar.HorizontalSection(); + + % Create each section + for sIdx = 1:numel(sizes) + section(sIdx) = wt.toolbar.HorizontalSection(); + section(sIdx).Title = "SECTION " + sIdx; + for bIdx = 1:sizes(sIdx) + section(sIdx).addButton("", string(bIdx)); + end + end + + % Attach the sections to the toolbar + testCase.verifySetProperty("Section", section) + + end %function + + end % methods + + + %% Unit Tests + methods (Test) + + function testHorizontalSection(testCase) + + % Add a single horizontal section (always required) + fcn = @()wt.toolbar.HorizontalSection(); + section = verifyWarningFree(testCase, fcn); + + % Add buttons + testCase.verifyAddButton(section,"Open","open_24.png"); + testCase.verifyAddButton(section,"Save","save_24.png"); + + % Set the section title + newValue = "MY HORIZONTAL SECTION"; + section.Title = newValue; + + % Attach the sections to the toolbar + testCase.verifySetProperty("Section", section) + + % Verify the label changed + actValue = testCase.Widget.SectionLabel(1).Text; + testCase.verifyMatches(newValue, actValue) + + % Verify the section width + testCase.verifyNumElements(testCase.Widget.Grid.ColumnWidth, 2) + sectionWidth = testCase.Widget.Grid.ColumnWidth{1}; + minWidth = 99; %pixels + testCase.verifyGreaterThan(sectionWidth, minWidth) + + end %function + + + function testStateButtons(testCase) + + % Add a single horizontal section (always required) + fcn = @()wt.toolbar.HorizontalSection(); + section = verifyWarningFree(testCase, fcn); + + % Add buttons + b1 = testCase.verifyAddStateButton(section,"Play","play_24.png"); + b2 = testCase.verifyAddStateButton(section,"Stop","stop_24.png"); + + % Attach the sections to the toolbar + testCase.verifySetProperty("Section", section) + + % Press a state button + testCase.ButtonPushCallbackDone = false; + testCase.press(b1) + + % Verify the callback was fired + testCase.verifyTrue(testCase.ButtonPushCallbackDone); + + % Verify states + testCase.verifyTrue(b1.Value) + testCase.verifyFalse(b2.Value) + + % Press the other state button + testCase.ButtonPushCallbackDone = false; + testCase.press(b2) + + % Verify the callback was fired + testCase.verifyTrue(testCase.ButtonPushCallbackDone); + + % Verify states + testCase.verifyTrue(b1.Value) + testCase.verifyTrue(b2.Value) + + end %function + + + function testVerticalSection(testCase) + + % Add a single horizontal section (always required) + fcn = @()wt.toolbar.HorizontalSection(); + section = verifyWarningFree(testCase, fcn); + + % Add vertical section + sectionV = testCase.verifyAddVerticalSection(section); + + % Add buttons + testCase.verifyAddButton(sectionV,"Play","play_24.png"); + testCase.verifyAddButton(sectionV,"Stop","stop_24.png"); + + % Attach the sections to the toolbar + testCase.verifySetProperty("Section", section) + + % Verify the section width + testCase.verifyNumElements(testCase.Widget.Grid.ColumnWidth, 2) + sectionWidth = testCase.Widget.Grid.ColumnWidth{1}; + minWidth = 75; %pixels + testCase.verifyGreaterThan(sectionWidth, minWidth) + + % Verify the section heights + expValue = {'fit' 'fit'}; + actValue = sectionV.Grid.RowHeight; + testCase.verifyEqual(actValue, expValue) + + end %function + + + function testCustomComponent(testCase) + + % Add a single horizontal section (always required) + fcn = @()wt.toolbar.HorizontalSection(); + section = verifyWarningFree(testCase, fcn); + + % Add custom component to horizontal section + customA = uiimage("Parent",[]); + section.Component(end+1) = customA; + + % Add vertical section + sectionV = testCase.verifyAddVerticalSection(section); + + % Add button + testCase.verifyAddButton(sectionV,"Play","play_24.png"); + + % Add custom component to vertical section + customB = uislider("Parent",[]); + sectionV.Component(end+1) = customB; + + % Attach the sections to the toolbar + testCase.verifySetProperty("Section", section) + + % Verify the component placement + testCase.verifyEqual(customA.Parent, section.Grid) + testCase.verifyEqual(customB.Parent, sectionV.Grid) + + % Verify the section width + testCase.verifyNumElements(testCase.Widget.Grid.ColumnWidth, 2) + sectionWidth = testCase.Widget.Grid.ColumnWidth{1}; + minWidth = 150; %pixels + testCase.verifyGreaterThan(sectionWidth, minWidth) + + % Verify the section heights + expValue = {'fit' 'fit'}; + actValue = sectionV.Grid.RowHeight; + testCase.verifyEqual(actValue, expValue) + + end %function + + + function testSectionPopout(testCase) + + % Add multiple section + sizes = [4 3 5 1]; + section = testCase.addMultiSections(sizes); + + % Grab the toolbar section buttons (internal) + sb = testCase.Widget.SectionButton; + + % Sections 1 & 2 should be visible + testCase.verifyEqual(section(1).Parent, testCase.Widget.Grid) + testCase.verifyEqual(section(2).Parent, testCase.Widget.Grid) + testCase.verifyTrue(section(1).Visible) + testCase.verifyTrue(section(2).Visible) + testCase.verifyFalse(sb(1).Visible) + testCase.verifyFalse(sb(2).Visible) + + % Section 3 should be hidden by a button (too big) + testCase.verifyEqual(section(3).Parent, testCase.Widget.Grid) + testCase.verifyFalse(section(3).Visible) + testCase.verifyTrue(sb(3).Visible) + + % Pop out section 3 + testCase.press(sb(3)) + + % SectionIsOpen is correct - NO sections open + expVal = [false false true false]'; + testCase.verifyEqual(testCase.Widget.SectionIsOpen, expVal); + + % Section 3 should be popped out below, parented to the figure + testCase.verifyEqual(section(3).Parent, testCase.Figure) + testCase.verifyTrue(section(3).Visible) + testCase.verifyTrue(sb(3).Visible) + + % Section 3 popout is positioned below the button + posS = section(3).Position; + posB = getpixelposition(sb(3),true); + yBotB = posB(2); + yTopS = posS(2) + posS(4); + testCase.verifyLessThanOrEqual(yTopS, yBotB) + + % Section 3 popout is fully within the figure + figPos = testCase.Figure.Position; + testCase.verifyLessThan(posS(3), figPos(3)) + testCase.verifyLessThan(posS(4), figPos(4)) + testCase.verifyGreaterThan(posS(1), 0) + testCase.verifyGreaterThan(posS(2), 0) + + % Press the last button in the popout + button = section(3).Component(end); + testCase.ButtonPushCallbackDone = false; + testCase.press(button) + + % Verify the callback was fired + testCase.verifyTrue(testCase.ButtonPushCallbackDone); + + % Section 3 should be closed and hidden by button again + testCase.verifyEqual(section(3).Parent, testCase.Widget.Grid) + testCase.verifyFalse(section(3).Visible) + testCase.verifyTrue(sb(3).Visible) + + % SectionIsOpen is correct - Section 3 open + expVal = [false false false false]'; + testCase.verifyEqual(testCase.Widget.SectionIsOpen, expVal); + + % Pop out section 3 again + testCase.press(sb(3)) + + % SectionIsOpen is correct + expVal = [false false true false]'; + testCase.verifyEqual(testCase.Widget.SectionIsOpen, expVal); + + % Press a button in another section + button = testCase.Widget.Section(1).Component(1); + testCase.ButtonPushCallbackDone = false; + testCase.press(button) + + % Verify the callback was fired + testCase.verifyTrue(testCase.ButtonPushCallbackDone); + + % SectionIsOpen is correct - NO sections open + expVal = [false false false false]'; + testCase.verifyEqual(testCase.Widget.SectionIsOpen, expVal); + + end %function + + end %methods (Test) + +end %classdef \ No newline at end of file diff --git a/widgets/+wt/+toolbar/BaseSection.m b/widgets/+wt/+toolbar/BaseSection.m index 23e87ca..d2067be 100644 --- a/widgets/+wt/+toolbar/BaseSection.m +++ b/widgets/+wt/+toolbar/BaseSection.m @@ -3,12 +3,12 @@ BaseSection < wt.abstract.BaseWidget & wt.mixin.FontStyled % Base class for a toolbar section - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Events %events (HasCallbackProperty, NotifyAccess = protected) - %RAJ - g2405728 + %RAJ - manually added callback due to g2405728 events (NotifyAccess = protected) % Event triggered when a button is pushed diff --git a/widgets/+wt/+toolbar/HorizontalSection.m b/widgets/+wt/+toolbar/HorizontalSection.m index cc19059..9ab788b 100644 --- a/widgets/+wt/+toolbar/HorizontalSection.m +++ b/widgets/+wt/+toolbar/HorizontalSection.m @@ -1,7 +1,7 @@ classdef (Sealed, Hidden) HorizontalSection < wt.toolbar.BaseSection % A row of horizontally stacked controls for a toolbar - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Public properties @@ -43,7 +43,7 @@ properties (Dependent, SetAccess = protected) % Indicates which components are vertical sections - IsVerticalSection + IsVerticalSection (:,1) logical end %properties @@ -161,8 +161,12 @@ function onButtonPushed(obj,evt) function value = get.IsVerticalSection(obj) - types = get(obj.Component,'Type'); - value = types == lower("wt.toolbar.VerticalSection"); + if isempty(obj.Component) + value = false(0,1); + else + types = get(obj.Component,'Type'); + value = types == lower("wt.toolbar.VerticalSection"); + end end diff --git a/widgets/+wt/+toolbar/VerticalSection.m b/widgets/+wt/+toolbar/VerticalSection.m index 2d19e62..5480df1 100644 --- a/widgets/+wt/+toolbar/VerticalSection.m +++ b/widgets/+wt/+toolbar/VerticalSection.m @@ -1,7 +1,7 @@ classdef (Sealed, Hidden) VerticalSection < wt.toolbar.BaseSection % A column of vertically stacked controls for a toolbar - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Public properties diff --git a/widgets/+wt/+utility/cleanPath.m b/widgets/+wt/+utility/cleanPath.m index c7899ab..58630ad 100644 --- a/widgets/+wt/+utility/cleanPath.m +++ b/widgets/+wt/+utility/cleanPath.m @@ -24,7 +24,7 @@ % "C:\Program Files\MATLAB" % -% Copyright 2020 The MathWorks Inc. +% Copyright 2020-2021 The MathWorks Inc. % --------------------------------------------------------------------- % File separator - in case of regional variants diff --git a/widgets/+wt/+utility/fastSet.m b/widgets/+wt/+utility/fastSet.m index 3f5a0d8..3c9a5ae 100644 --- a/widgets/+wt/+utility/fastSet.m +++ b/widgets/+wt/+utility/fastSet.m @@ -17,7 +17,7 @@ function fastSet(obj,varargin) % none % -% Copyright 2020 The MathWorks Inc. +% Copyright 2020-2021 The MathWorks Inc. % --------------------------------------------------------------------- for oIdx = 1:numel(obj) diff --git a/widgets/+wt/+validators/mustBeBetween.m b/widgets/+wt/+validators/mustBeBetween.m index 0e245a4..557221b 100644 --- a/widgets/+wt/+validators/mustBeBetween.m +++ b/widgets/+wt/+validators/mustBeBetween.m @@ -7,7 +7,7 @@ function mustBeBetween(A,minA,maxA) % end % -% Copyright 2020 The MathWorks, Inc. +% Copyright 2020-2021 The MathWorks, Inc. if any(A(:) > maxA | A(:) < minA) error('wt:validators:mustBeBetween',... diff --git a/widgets/+wt/+validators/mustBeBetweenZeroAnd100.m b/widgets/+wt/+validators/mustBeBetweenZeroAnd100.m index 94f30f7..d363705 100644 --- a/widgets/+wt/+validators/mustBeBetweenZeroAnd100.m +++ b/widgets/+wt/+validators/mustBeBetweenZeroAnd100.m @@ -6,6 +6,6 @@ function mustBeBetweenZeroAnd100(A) % PropertyName dataType {wt.validators.mustBeBetweenZeroAnd100} % end -% Copyright 2020 The MathWorks, Inc. +% Copyright 2020-2021 The MathWorks, Inc. wt.validators.mustBeBetween(A,0,100); \ No newline at end of file diff --git a/widgets/+wt/+validators/mustBeBetweenZeroAndOne.m b/widgets/+wt/+validators/mustBeBetweenZeroAndOne.m index d18b511..c9690ac 100644 --- a/widgets/+wt/+validators/mustBeBetweenZeroAndOne.m +++ b/widgets/+wt/+validators/mustBeBetweenZeroAndOne.m @@ -6,6 +6,6 @@ function mustBeBetweenZeroAndOne(A) % PropertyName dataType {wt.validators.mustBeBetweenZeroAndOne} % end -% Copyright 2020 The MathWorks, Inc. +% Copyright 2020-2021 The MathWorks, Inc. wt.validators.mustBeBetween(A,0,1); \ No newline at end of file diff --git a/widgets/+wt/ButtonGrid.m b/widgets/+wt/ButtonGrid.m index fabfd96..b96c938 100644 --- a/widgets/+wt/ButtonGrid.m +++ b/widgets/+wt/ButtonGrid.m @@ -2,7 +2,7 @@ wt.mixin.Enableable & wt.mixin.FontStyled & wt.mixin.ButtonColorable % A grid of buttons with a single callback/event - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Events @@ -58,7 +58,7 @@ properties ( Transient, NonCopyable, ... Access = {?wt.abstract.BaseWidget, ?wt.test.BaseWidgetTest} ) - % Buttons + % Buttons (other widgets like ListSelector also access this) Button (1,:) matlab.ui.control.Button end %properties diff --git a/widgets/+wt/CheckboxList.m b/widgets/+wt/CheckboxList.m index 88670db..d14a423 100644 --- a/widgets/+wt/CheckboxList.m +++ b/widgets/+wt/CheckboxList.m @@ -2,7 +2,7 @@ wt.mixin.Enableable & wt.mixin.FontStyled & wt.mixin.Tooltipable % A checkbox list - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Public properties diff --git a/widgets/+wt/ColorSelector.m b/widgets/+wt/ColorSelector.m index e9dd27a..91539e5 100644 --- a/widgets/+wt/ColorSelector.m +++ b/widgets/+wt/ColorSelector.m @@ -3,7 +3,7 @@ wt.mixin.FieldColorable % A color selection control with browse button - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Public properties diff --git a/widgets/+wt/FileSelector.m b/widgets/+wt/FileSelector.m index bcca57b..798a21e 100644 --- a/widgets/+wt/FileSelector.m +++ b/widgets/+wt/FileSelector.m @@ -3,7 +3,7 @@ wt.mixin.FieldColorable & wt.mixin.ButtonColorable % A file/folder selection control with browse button - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Public properties diff --git a/widgets/+wt/ListSelector.m b/widgets/+wt/ListSelector.m index 6f0e99c..6bbc465 100644 --- a/widgets/+wt/ListSelector.m +++ b/widgets/+wt/ListSelector.m @@ -2,7 +2,7 @@ wt.mixin.FontStyled & wt.mixin.ButtonColorable % Select from an array of items and add them to a list - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Events diff --git a/widgets/+wt/ListSelectorTwoPane.m b/widgets/+wt/ListSelectorTwoPane.m index d79ef73..3467258 100644 --- a/widgets/+wt/ListSelectorTwoPane.m +++ b/widgets/+wt/ListSelectorTwoPane.m @@ -1,7 +1,7 @@ classdef ListSelectorTwoPane < wt.ListSelector % Select from an array of items and add them to a list - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. diff --git a/widgets/+wt/PasswordField.m b/widgets/+wt/PasswordField.m index b8cdfb2..6fef89e 100644 --- a/widgets/+wt/PasswordField.m +++ b/widgets/+wt/PasswordField.m @@ -1,7 +1,7 @@ classdef PasswordField < wt.abstract.BaseWidget % A password entry field - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Public properties diff --git a/widgets/+wt/ProgressBar.m b/widgets/+wt/ProgressBar.m index fe8bd49..3466a47 100644 --- a/widgets/+wt/ProgressBar.m +++ b/widgets/+wt/ProgressBar.m @@ -1,7 +1,7 @@ classdef ProgressBar < wt.abstract.BaseWidget & wt.mixin.FontStyled % A progress bar with status and cancel button - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Events @@ -11,7 +11,6 @@ %% Properties - properties (AbortSet) % Shows time remaining during progress @@ -67,8 +66,10 @@ end %properties + %% Internal Properties - properties (Access = protected) + properties ( Transient, NonCopyable, ... + Access = {?wt.TaskStatusTable, ?wt.test.BaseWidgetTest} ) % Progress panel ProgressPanel (1,1) matlab.ui.container.Panel @@ -172,13 +173,13 @@ function cancel(obj) obj.update(); + % Process updates immediately + drawnow + % Trigger event evt = wt.eventdata.ButtonPushedData(obj.CancelButton, "Cancel"); notify(obj,"CancelPressed",evt); - % Process updates immediately - drawnow - end %function @@ -236,6 +237,7 @@ function setup(obj) % Progress panel indicator obj.ProgressPanel = matlab.ui.container.Panel(... "Parent",obj.Grid,... + "Units","pixels",... "BackgroundColor",[.5 .7 1],... "BorderType","none"); obj.ProgressPanel.Layout.Column = 1; @@ -287,34 +289,43 @@ function setup(obj) function update(obj) % Toggle visibilities - obj.RemTimeLabel.Visible = obj.ShowTimeRemaining; + remTimeVisible = obj.ShowTimeRemaining; + wt.utility.fastSet(obj.RemTimeLabel,"Visible",remTimeVisible); % Show/hide the indeterminate bar - obj.IndeterminateBar.Visible = obj.Indeterminate && obj.Running; + indBarVisible = obj.Indeterminate && obj.Running; + wt.utility.fastSet(obj.IndeterminateBar,"Visible",indBarVisible); % Update the progress indication by adjusting the beneath grid colWidths = [cellstr([obj.Value 1-obj.Value] + "x") {0}]; % Update the status text - obj.StatusTextLabel.Text = " " + obj.StatusText; + if strlength(obj.StatusText) + statusText = " " + obj.StatusText; + else + statusText = ""; + end + wt.utility.fastSet(obj.StatusTextLabel,"Text",statusText); % Update remaining time if obj.ShowTimeRemaining && ~isinf(obj.RemainingTime) - obj.RemTimeLabel.Text = string(obj.RemainingTime); + timeText = string(obj.RemainingTime); + else + timeText = ""; end + wt.utility.fastSet(obj.RemTimeLabel,"Text",timeText); % Update cancel button - if obj.ShowCancel && obj.Running - %obj.CancelButton.Visible = true; + cancelVisible = obj.ShowCancel && obj.Running; + cancelColor = "none"; + if cancelVisible colWidths{3} = 25; if obj.CancelRequested - obj.CancelButton.BackgroundColor = "red"; - else - obj.CancelButton.BackgroundColor = "none"; + cancelColor = "red"; end - elseif obj.CancelButton.Visible - %obj.CancelButton.Visible = false; + wt.utility.fastSet(obj.CancelButton,"BackgroundColor",cancelColor); end + wt.utility.fastSet(obj.CancelButton,"Visible",cancelVisible); % Update the grid obj.Grid.ColumnWidth = colWidths; diff --git a/widgets/+wt/SliderCheckboxGroup.m b/widgets/+wt/SliderCheckboxGroup.m index 790011f..6b75979 100644 --- a/widgets/+wt/SliderCheckboxGroup.m +++ b/widgets/+wt/SliderCheckboxGroup.m @@ -3,7 +3,7 @@ % A group of sliders with checkboxes, useful for visibility of various % layers of imagery - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Events diff --git a/widgets/+wt/SliderSpinner.m b/widgets/+wt/SliderSpinner.m index 741dc91..9155aa1 100644 --- a/widgets/+wt/SliderSpinner.m +++ b/widgets/+wt/SliderSpinner.m @@ -2,7 +2,7 @@ wt.mixin.Enableable & wt.mixin.FontStyled % A slider and spinner combination - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Public properties @@ -25,7 +25,7 @@ ValueDisplayFormat % Orientation of the spinner and slider - Orientation + Orientation (1,1) wt.enum.HorizontalVerticalState = wt.enum.HorizontalVerticalState.horizontal % Size of spinner (width for horizontal, height for vertical SpinnerSize @@ -281,14 +281,14 @@ function onSpinnerChanged(obj,e) function value = get.Orientation(obj) - value = obj.Slider.Orientation; + value = wt.enum.HorizontalVerticalState(obj.Slider.Orientation); end function set.Orientation(obj,value) - obj.Slider.Orientation = value; + obj.Slider.Orientation = char(value); obj.updateLayout(); end - + function value = get.MajorTicks(obj) value = obj.Slider.MajorTicks; diff --git a/widgets/+wt/TaskStatusTable.m b/widgets/+wt/TaskStatusTable.m index f112245..c4458bd 100644 --- a/widgets/+wt/TaskStatusTable.m +++ b/widgets/+wt/TaskStatusTable.m @@ -3,7 +3,7 @@ wt.mixin.ButtonColorable % A table showing status of multiple tasks - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Public properties @@ -61,7 +61,8 @@ %% Internal Properties - properties (Access = protected) + properties ( Transient, NonCopyable, ... + Access = {?wt.TaskStatusTable, ?wt.test.BaseWidgetTest} ) % Grid for task items TaskGrid (1,1) matlab.ui.container.GridLayout @@ -159,10 +160,6 @@ function update(obj) obj.Label(idx) = uilabel(obj.TaskGrid,"Text",""); end - % Update the internal component lists - obj.FontStyledComponents = [obj.Label, obj.StatusLabel]; - obj.EnableableComponents = [obj.Label, obj.Icon, obj.StatusLabel]; - elseif numOld > numNew % Remove rows @@ -173,6 +170,12 @@ function update(obj) end %if numNew > numOld + % Update the internal component lists + if numOld ~= numNew + obj.FontStyledComponents = [obj.Label, obj.StatusLabel]; + obj.EnableableComponents = [obj.Label, obj.Icon, obj.StatusLabel]; + end + % Update the task names and icons status = obj.Status; imgFile = string(status) + "_16.png"; @@ -244,6 +247,11 @@ function updateEnableableComponents(obj) function onBackButtonPushed(obj,evt) % Triggered on button pushed + % Go back a step + if ~isempty(obj.SelectedIndex) && obj.SelectedIndex > 1 + obj.SelectedIndex = obj.SelectedIndex - 1; + end + % Trigger event evtOut = wt.eventdata.ButtonPushedData(evt.Source, "Back"); notify(obj,"ButtonPushed",evtOut); @@ -254,6 +262,13 @@ function onBackButtonPushed(obj,evt) function onForwardButtonPushed(obj,evt) % Triggered on button pushed + % Go forward a step + if isempty(obj.SelectedIndex) + obj.SelectedIndex = 1; + elseif obj.SelectedIndex < numel(obj.Items) + obj.SelectedIndex = obj.SelectedIndex + 1; + end + % Trigger event evtOut = wt.eventdata.ButtonPushedData(evt.Source, "Forward"); notify(obj,"ButtonPushed",evtOut); diff --git a/widgets/+wt/Toolbar.m b/widgets/+wt/Toolbar.m index d5d0313..bebfc9d 100644 --- a/widgets/+wt/Toolbar.m +++ b/widgets/+wt/Toolbar.m @@ -2,7 +2,7 @@ & wt.mixin.FontStyled % A configurable toolbar - % Copyright 2020 The MathWorks Inc. + % Copyright 2020-2021 The MathWorks Inc. %% Events events (HasCallbackProperty, NotifyAccess = protected) @@ -33,7 +33,7 @@ %% Internal Properties - properties %RAJ(GetAccess = protected, SetAccess = private) + properties (Transient, NonCopyable, Access = {?wt.test.BaseWidgetTest}) % The listbox control ListBox (1,1) matlab.ui.control.ListBox @@ -56,13 +56,18 @@ % Listen to button pushes in sections ButtonPushedListener event.listener - % Sections that are open outside the toolbar - OpenSection wt.toolbar.HorizontalSection + end %properties + + + properties (Dependent, NonCopyable, Access = {?wt.test.BaseWidgetTest}) + + % Indicates which sections are open + SectionIsOpen (:,1) logical end %properties - properties (Constant, Access = protected) + properties (Constant, Access = private) % The down arrow mask for the button icons BUTTON_MASK (:,:) logical = sectionButtonIconMask() @@ -99,9 +104,6 @@ function setup(obj) % Add a dummy section to color the empty space obj.DummySection = uicontainer(obj.Grid); - %obj.DummySection = uipanel(obj.Grid); - %obj.DummySection.BorderType = 'none'; - %obj.DummySection.Title = ''; obj.DummySection.Layout.Row = [1 2]; obj.BackgroundColorableComponents = obj.DummySection; @@ -199,9 +201,6 @@ function update(obj) function updateLayout(obj) % Dynamically configure the toolbar based on space - - % Close any open sections - obj.closePanel(); % Return if no sections to show if isempty(obj.Section) @@ -307,8 +306,8 @@ function updateButtonIcons(obj) function onButtonPushed(obj,evt) - % Close any open panel - obj.closePanel(); + % Close any open sections + obj.updateLayout(); % Trigger event notify(obj,"ButtonPushed",evt); @@ -327,56 +326,56 @@ function onPanelButtonPushed(obj,e) % Which panel? sectionButton = e.Source; - section = obj.Section(obj.SectionButton == sectionButton); - - % Is it the button for a panel that's already open? If so, just - % close it and return - if isequal(section, obj.OpenSection) - obj.closePanel(); - return + isActivatedSection = obj.SectionButton == sectionButton; + section = obj.Section(isActivatedSection); + + % Get the status of section panels to change + isOpenSection = obj.SectionIsOpen; + isDeactivating = isOpenSection & ~isActivatedSection; + isActivating = ~isOpenSection & isActivatedSection; + + % Is the pushed section already open? + if any(isOpenSection(isActivatedSection)) + % Instead update the layout to closse it + obj.updateLayout(); + return end - % Close any open sections - obj.closePanel(); - - - % Where are things located now? - bPos = getpixelposition(sectionButton, true); - fig = ancestor(obj,'figure'); - figPos = getpixelposition(fig); - figureWidth = figPos(3); - - % Where should the panel go? - panelX = bPos(1); - panelWidth = section.TotalWidth; - panelHeight = bPos(4) - obj.Grid.RowHeight{2} - obj.Grid.RowSpacing; - panelY = bPos(2) - panelHeight; - - % Adjust panel X position if needed - panelRightEdge = panelX + panelWidth; - if panelRightEdge > figureWidth - panelX = figureWidth - panelWidth; + % Close any deactivated sections + if any(isDeactivating) + set(obj.Section(isDeactivating), 'Visible', 'off'); end - % Now, position and show the panel as a dropdown - panelPos = [panelX panelY panelWidth panelHeight]; - obj.OpenSection = section; - section.Parent = fig; - setpixelposition(section, panelPos, true); - section.Visible = 'on'; - - end %function - - - function closePanel(obj) - - % Is a section panel open? - if ~isempty(obj.OpenSection) + % Activate the specified section + if any(isActivating) + + % Where are things located now? + bPos = getpixelposition(sectionButton, true); + fig = ancestor(obj,'figure'); + wt.utility.fastSet(fig,"Units","pixels"); + figureWidth = fig.Position(3); + + % Where should the panel go? + panelX = bPos(1); + panelWidth = section.TotalWidth; + panelHeight = bPos(4) - obj.Grid.RowHeight{2} - obj.Grid.RowSpacing; + panelY = bPos(2) - panelHeight - 1; - obj.OpenSection.Parent = []; - obj.OpenSection(:) = []; + % Adjust panel X position if needed + panelRightEdge = panelX + panelWidth; + if panelRightEdge > figureWidth + panelX = figureWidth - panelWidth; + end + + % Now, position and show the panel as a dropdown + panelPos = [panelX panelY panelWidth panelHeight]; - end %if ~isempty(obj.OpenSection) + % Turn it on + set(obj.Section(isActivating), 'Parent', fig); + set(obj.Section(isActivating), 'Position', panelPos); + set(obj.Section(isActivating), 'Visible', 'on'); + + end %if any(isActivating) end %function @@ -393,6 +392,13 @@ function closePanel(obj) obj.Grid.BackgroundColor = value; end + function value = get.SectionIsOpen(obj) + value = false(size(obj.Section)); + for idx = 1:numel(obj.Section) + value(idx) = ~isequal(obj.Section(idx).Parent, obj.Grid); + end + end + end %methods diff --git a/widgets/Contents.m b/widgets/Contents.m index a527b0b..a041e66 100644 --- a/widgets/Contents.m +++ b/widgets/Contents.m @@ -1,4 +1,4 @@ % Widgets Toolbox - MATLAB App Building Components -% Version 2.0.0 (R2020b) 24-Nov-2020 +% Version 2.0.3 (R2020b) 06-Jan-2021 % -% Copyright 2020 The MathWorks Inc. +% Copyright 2020-2021 The MathWorks Inc. diff --git a/widgets/examples/WidgetExamples.mlx b/widgets/examples/WidgetExamples.mlx index 979a749..a9021cd 100644 Binary files a/widgets/examples/WidgetExamples.mlx and b/widgets/examples/WidgetExamples.mlx differ diff --git a/widgets/examples/html/WidgetExamples.html b/widgets/examples/html/WidgetExamples.html index 6f41efa..30d4f39 100644 --- a/widgets/examples/html/WidgetExamples.html +++ b/widgets/examples/html/WidgetExamples.html @@ -31,7 +31,7 @@ .S9 { border-left: 1px solid rgb(233, 233, 233); border-right: 1px solid rgb(233, 233, 233); border-top: 1px solid rgb(233, 233, 233); border-bottom: 1px solid rgb(233, 233, 233); border-radius: 4px 4px 0px 0px; padding: 6px 45px 4px 13px; line-height: 17.234px; min-height: 18px; white-space: nowrap; color: rgb(0, 0, 0); font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; } .S10 { border-left: 1px solid rgb(233, 233, 233); border-right: 1px solid rgb(233, 233, 233); border-top: 0px none rgb(0, 0, 0); border-bottom: 1px solid rgb(233, 233, 233); border-radius: 0px 0px 4px 4px; padding: 0px 45px 4px 13px; line-height: 17.234px; min-height: 18px; white-space: nowrap; color: rgb(0, 0, 0); font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; } .S11 { margin: 10px 10px 9px 4px; padding: 0px; line-height: 21px; min-height: 0px; white-space: pre-wrap; color: rgb(0, 0, 0); font-family: Helvetica, Arial, sans-serif; font-style: normal; font-size: 14px; font-weight: 400; text-align: left; } -.S12 { margin: 15px 10px 5px 4px; padding: 0px; line-height: 18px; min-height: 0px; white-space: pre-wrap; color: rgb(60, 60, 60); font-family: Helvetica, Arial, sans-serif; font-style: normal; font-size: 17px; font-weight: 700; text-align: left; }

Widgets Toolbox - MATLAB App Building Components - Examples

Copyright 2020 The MathWorks, Inc.

Button Grid

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 75]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
buttonGridWidget = wt.ButtonGrid(gridObj);
% Configure the widget
buttonGridWidget.BackgroundColor = [.6 .8 1];
% Add optional icons
buttonGridWidget.Icon = [
"add_24.png"
"delete_24.png"
"play_24.png"
"pause_24.png"
"stop_24.png"
];
% Add optional text
buttonGridWidget.Text = [
"Add"
"Delete"
"Play"
"Pause"
"Stop"
];
% Add optional tooltip
buttonGridWidget.Tooltip = [
"Press to Add"
"Press to Delete"
"Press to Play"
"Press to Pause"
"Press to Stop"
];
% Assign a callback
buttonGridWidget.ButtonPushedFcn = @(h,e)disp(e)
buttonGridWidget =
ButtonGrid with properties: +.S12 { margin: 15px 10px 5px 4px; padding: 0px; line-height: 18px; min-height: 0px; white-space: pre-wrap; color: rgb(60, 60, 60); font-family: Helvetica, Arial, sans-serif; font-style: normal; font-size: 17px; font-weight: 700; text-align: left; }

Widgets Toolbox - MATLAB App Building Components - Examples

Copyright 2020-2021 The MathWorks, Inc.

Button Grid

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 75]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
buttonGridWidget = wt.ButtonGrid(gridObj);
% Configure the widget
buttonGridWidget.BackgroundColor = [.6 .8 1];
% Add optional icons
buttonGridWidget.Icon = [
"add_24.png"
"delete_24.png"
"play_24.png"
"pause_24.png"
"stop_24.png"
];
% Add optional text
buttonGridWidget.Text = [
"Add"
"Delete"
"Play"
"Pause"
"Stop"
];
% Add optional tooltip
buttonGridWidget.Tooltip = [
"Press to Add"
"Press to Delete"
"Press to Play"
"Press to Pause"
"Press to Stop"
];
% Assign a callback
buttonGridWidget.ButtonPushedFcn = @(h,e)disp(e)
buttonGridWidget =
ButtonGrid with properties: Icon: ["add_24.png" "delete_24.png" "play_24.png" "pause_24.png" "stop_24.png"] Text: ["Add" "Delete" "Play" "Pause" "Stop"] @@ -46,7 +46,7 @@ Position: [11 11 230 55] Show all properties -

Checkbox List

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 200]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
cbListWidget = wt.CheckboxList(gridObj);
% Configure the widget
cbListWidget.Items = [
"Boston"
"Chicago"
"Houston"
"Los Angeles"
"Miami"
"New York"
"Seattle"
];
cbListWidget.Value(3) = false;
% This is optional (default is false)
cbListWidget.ShowSelectAll = true;
% Assign a callback
cbListWidget.ValueChangedFcn = @(h,e)disp(e)
cbListWidget =
CheckboxList with properties: +

Checkbox List

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 200]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
cbListWidget = wt.CheckboxList(gridObj);
% Configure the widget
cbListWidget.Items = [
"Boston"
"Chicago"
"Houston"
"Los Angeles"
"Miami"
"New York"
"Seattle"
];
cbListWidget.Value(3) = false;
% This is optional (default is false)
cbListWidget.ShowSelectAll = true;
% Assign a callback
cbListWidget.ValueChangedFcn = @(h,e)disp(e)
cbListWidget =
CheckboxList with properties: Items: [7×1 string] Value: [7×1 logical] @@ -55,7 +55,7 @@ Position: [100 100 100 130] Show all properties -

Color Selector

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 45]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
colorWidget = wt.ColorSelector(gridObj);
% Configure the widget
colorWidget.Value = [1 0 0];
% Assign a callback
colorWidget.ValueChangedFcn = @(h,e)disp(e)
colorWidget =
ColorSelector with properties: +

Color Selector

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 45]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
colorWidget = wt.ColorSelector(gridObj);
% Configure the widget
colorWidget.Value = [1 0 0];
% Assign a callback
colorWidget.ValueChangedFcn = @(h,e)disp(e)
colorWidget =
ColorSelector with properties: Value: [1 0 0] ShowEditField: on @@ -63,13 +63,13 @@ Position: [100 100 100 25] Show all properties -
If you want to have only the button with no edit field:
% Change the color
colorWidget.Value = [0 1 0];
% Turn off the edit field
colorWidget.ShowEditField = false;
% Make it a sensible width
gridObj.ColumnWidth = {27}
gridObj =
GridLayout with properties: +
If you want to have only the button with no edit field:
% Change the color
colorWidget.Value = [0 1 0];
% Turn off the edit field
colorWidget.ShowEditField = false;
% Make it a sensible width
gridObj.ColumnWidth = {27}
gridObj =
GridLayout with properties: RowHeight: {'1x'} ColumnWidth: {[27]} Show all properties -

File Selector

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 45]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
fileWidget = wt.FileSelector(gridObj);
% Configure the widget
fileWidget.Value = matlabroot;
% Assign a callback
fileWidget.ValueChangedFcn = @(h,e)disp(e)
fileWidget =
FileSelector with properties: +

File Selector

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 45]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
fileWidget = wt.FileSelector(gridObj);
% Configure the widget
fileWidget.Value = matlabroot;
% Assign a callback
fileWidget.ValueChangedFcn = @(h,e)disp(e)
fileWidget =
FileSelector with properties: Value: "C:\Program Files\MATLAB\R2021a_BASH" FullPath: "C:\Program Files\MATLAB\R2021a_BASH" @@ -83,7 +83,7 @@ Position: [100 100 200 25] Show all properties -
If you want to have a dropdown button to show recent history:
fileWidget.ShowHistory = true;

List Selector (single pane)

The List Selector widget is intended for adding items to a list from a known set. You can optionally enable reordering the items, and add custom buttons.
% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 215]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
listWidget = wt.ListSelector(gridObj);
listWidget.Items = ["California","Massachusetts","Michigan","Texas"];
listWidget.Value = ["Massachusetts","Michigan"];
% Assign a callback
listWidget.ValueChangedFcn = @(h,e)disp(e)
listWidget =
ListSelector with properties: +
If you want to have a dropdown button to show recent history:
fileWidget.ShowHistory = true;

List Selector (single pane)

The List Selector widget is intended for adding items to a list from a known set. You can optionally enable reordering the items, and add custom buttons.
% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 215]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
listWidget = wt.ListSelector(gridObj);
listWidget.Items = ["California","Massachusetts","Michigan","Texas"];
listWidget.Value = ["Massachusetts","Michigan"];
% Assign a callback
listWidget.ValueChangedFcn = @(h,e)disp(e)
listWidget =
ListSelector with properties: Items: ["California" "Massachusetts" "Michigan" "Texas"] ItemsData: [1×0 double] @@ -100,7 +100,7 @@ Position: [11 11 230 195] Show all properties -
You can also add your own custom buttons:
listWidget.UserButtons.Icon = ["plot_24.png","play_24.png"];
listWidget.UserButtons.ButtonHeight = {25 25};
listWidget.ButtonPushedFcn = @(h,e)disp(e);

List Selector Two Pane

The List Selector Two-Pane widget is similar to the one-pane variant. It is intended for adding items to a list from a known set. In this case, the set of available items is displayed in a list on the left side, and items are selectively added to the right list.
Again you can optionally enable reordering the items and add custom buttons.
% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 300 215]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
listWidget2 = wt.ListSelectorTwoPane(gridObj);
listWidget2.Items = ["California","Massachusetts","Michigan","Texas"];
listWidget2.Value = ["Massachusetts","Michigan"];

Password Field

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 45]);
gridObj = uigridlayout(figObj,[1 2],"BackgroundColor",[.6 .8 1]);
gridObj.ColumnWidth = {'fit','1x'};
% Add label
uilabel(gridObj,"Text","Password:");
This component won't show in the preview, but it will create a password field with text displayed as dots.
% Create the widget
passwordWidget = wt.PasswordField(gridObj);
% Configure the widget
passwordWidget.Value = "MathWorks";

Progress Bar

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 300 60]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
progressWidget = wt.ProgressBar(gridObj);
% Configure the widget
progressWidget.ShowCancel = true;
% Assign a callback
progressWidget.CancelPressedFcn = @(h,e)disp("Cancel Pressed!")
progressWidget =
ProgressBar with properties: +
You can also add your own custom buttons:
listWidget.UserButtons.Icon = ["plot_24.png","play_24.png"];
listWidget.UserButtons.ButtonHeight = {25 25};
listWidget.ButtonPushedFcn = @(h,e)disp(e);

List Selector Two Pane

The List Selector Two-Pane widget is similar to the one-pane variant. It is intended for adding items to a list from a known set. In this case, the set of available items is displayed in a list on the left side, and items are selectively added to the right list.
Again you can optionally enable reordering the items and add custom buttons.
% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 300 215]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
listWidget2 = wt.ListSelectorTwoPane(gridObj);
listWidget2.Items = ["California","Massachusetts","Michigan","Texas"];
listWidget2.Value = ["Massachusetts","Michigan"];

Password Field

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 45]);
gridObj = uigridlayout(figObj,[1 2],"BackgroundColor",[.6 .8 1]);
gridObj.ColumnWidth = {'fit','1x'};
% Add label
uilabel(gridObj,"Text","Password:");
This component won't show in the preview, but it will create a password field with text displayed as dots.
% Create the widget
passwordWidget = wt.PasswordField(gridObj);
% Configure the widget
passwordWidget.Value = "MathWorks";

Progress Bar

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 300 60]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
progressWidget = wt.ProgressBar(gridObj);
% Configure the widget
progressWidget.ShowCancel = true;
% Assign a callback
progressWidget.CancelPressedFcn = @(h,e)disp("Cancel Pressed!")
progressWidget =
ProgressBar with properties: ShowTimeRemaining: on ShowCancel: on @@ -117,7 +117,7 @@ Position: [100 100 200 30] Show all properties -
% Start the progress bar
progressWidget.startProgress("The task is starting...");
% Wait some time to simulate a running task
pause(2)
% Update the task progress
progressWidget.setProgress(0.5, "The task is running...");
Then, when the task is done:
% Wait some time to simulate a running task
pause(2)
% Mark the task finished
progressWidget.finishProgress("The task is finished");
If you want an indeteriminate progress bar:
% Mark the task finished
progressWidget.Indeterminate = true;
% Start the progress bar
progressWidget.startProgress("The task is running...");

Slider Checkbox Combination Group

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 125]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
sliderCheckboxWidget = wt.SliderCheckboxGroup(gridObj);
% Configure the widget
sliderCheckboxWidget.Name = ["Red", "Green", "Blue", "Alpha"];
sliderCheckboxWidget.CheckboxWidth = 100
sliderCheckboxWidget =
SliderCheckboxGroup with properties: +
% Start the progress bar
progressWidget.startProgress("The task is starting...");
% Wait some time to simulate a running task
pause(2)
% Update the task progress
progressWidget.setProgress(0.5, "The task is running...");
Then, when the task is done:
% Mark the task finished
progressWidget.finishProgress("The task is finished");
If you want an indeteriminate progress bar:
% Mark the task finished
progressWidget.Indeterminate = true;
% Start the progress bar
progressWidget.startProgress("The task is running...");

Slider Checkbox Combination Group

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 125]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
sliderCheckboxWidget = wt.SliderCheckboxGroup(gridObj);
% Configure the widget
sliderCheckboxWidget.Name = ["Red", "Green", "Blue", "Alpha"];
sliderCheckboxWidget.CheckboxWidth = 100
sliderCheckboxWidget =
SliderCheckboxGroup with properties: Name: ["Red" "Green" "Blue" "Alpha"] State: [1 1 1 1] @@ -128,7 +128,7 @@ Position: [100 100 120 150] Show all properties -
sliderCheckboxWidget.Value = [0.8, 0.3, 0, 1];
sliderCheckboxWidget.State = [true, true, false, true];
% Assign a callback
sliderCheckboxWidget.ValueChangedFcn = @(h,e)disp(e)
sliderCheckboxWidget =
SliderCheckboxGroup with properties: +
sliderCheckboxWidget.Value = [0.8, 0.3, 0, 1];
sliderCheckboxWidget.State = [true, true, false, true];
% Assign a callback
sliderCheckboxWidget.ValueChangedFcn = @(h,e)disp(e)
sliderCheckboxWidget =
SliderCheckboxGroup with properties: Name: ["Red" "Green" "Blue" "Alpha"] State: [1 1 0 1] @@ -139,7 +139,7 @@ Position: [11 11 230 105] Show all properties -

Slider Spinner Combination

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 70]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
sliderSpinnerWidget = wt.SliderSpinner(gridObj);
% Configure the widget
sliderSpinnerWidget.Limits = [-10 30];
sliderSpinnerWidget.RoundFractionalValues = "off";
sliderSpinnerWidget.Step = 0.5;
sliderSpinnerWidget.Value = 16.5;
% Assign a callback
sliderSpinnerWidget.ValueChangedFcn = @(h,e)disp(e)
sliderSpinnerWidget =
SliderSpinner with properties: +

Slider Spinner Combination

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 70]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
sliderSpinnerWidget = wt.SliderSpinner(gridObj);
% Configure the widget
sliderSpinnerWidget.Limits = [-10 30];
sliderSpinnerWidget.RoundFractionalValues = "off";
sliderSpinnerWidget.Step = 0.5;
sliderSpinnerWidget.Value = 16.5;
% Assign a callback
sliderSpinnerWidget.ValueChangedFcn = @(h,e)disp(e)
sliderSpinnerWidget =
SliderSpinner with properties: Value: 16.5000 Limits: [-10 30] @@ -160,7 +160,7 @@ Position: [11 11 230 50] Show all properties -

Task Status Table

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 225]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
taskStatusWidget = wt.TaskStatusTable(gridObj);
% Configure the widget
taskStatusWidget.Items = [
"Import Data"
"Preprocess Data"
"Analyze Data"
"Plot Results"
"Save Results"
];
taskStatusWidget.Status = [
"complete"
"warning"
"running"
"none"
"none"
];
taskStatusWidget.SelectedIndex = 3;
% Assign a callback
taskStatusWidget.ButtonPushedFcn = @(h,e)disp(e)
taskStatusWidget =
TaskStatusTable with properties: +

Task Status Table

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 250 225]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
taskStatusWidget = wt.TaskStatusTable(gridObj);
% Configure the widget
taskStatusWidget.Items = [
"Import Data"
"Preprocess Data"
"Analyze Data"
"Plot Results"
"Save Results"
];
taskStatusWidget.Status = [
"complete"
"warning"
"running"
"none"
"none"
];
taskStatusWidget.SelectedIndex = 3;
% Assign a callback
taskStatusWidget.ButtonPushedFcn = @(h,e)disp(e)
taskStatusWidget =
TaskStatusTable with properties: Items: [5×1 string] Status: [5×1 wt.enum.StatusState] @@ -175,7 +175,7 @@ Position: [100 100 100 180] Show all properties -

Toolbar

Horizontal Sections

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 350 120]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
toolbarWidget1 = wt.Toolbar(gridObj);
% Create a horizontal section
section1 = wt.toolbar.HorizontalSection();
section1.Title = "NORMAL BUTTONS";
section1.addButton("open_24.png", "Open");
section1.addButton("save_24.png", "Save");
% Create a horizontal section with state buttons
section2 = wt.toolbar.HorizontalSection();
section2.Title = "STATE BUTTONS";
stateButton1 = section2.addStateButton("","Mode 1");
stateButton2 = section2.addStateButton("","Mode 2");
stateButton3 = section2.addStateButton("","Mode 3");
% Set the state of the buttons
stateButton1.Value = true;
stateButton2.Value = false;
stateButton3.Value = false;
% Attach the horizontal sections to the toolbar
toolbarWidget1.Section = [
section1
section2
];
% Assign a callback
toolbarWidget1.ButtonPushedFcn = @(h,e)disp(e)
toolbarWidget1 =
Toolbar with properties: +

Toolbar

Horizontal Sections

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 350 120]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
toolbarWidget1 = wt.Toolbar(gridObj);
% Create a horizontal section
section1 = wt.toolbar.HorizontalSection();
section1.Title = "NORMAL BUTTONS";
section1.addButton("open_24.png", "Open");
section1.addButton("save_24.png", "Save");
% Create a horizontal section with state buttons
section2 = wt.toolbar.HorizontalSection();
section2.Title = "STATE BUTTONS";
stateButton1 = section2.addStateButton("","Mode 1");
stateButton2 = section2.addStateButton("","Mode 2");
stateButton3 = section2.addStateButton("","Mode 3");
% Set the state of the buttons
stateButton1.Value = true;
stateButton2.Value = false;
stateButton3.Value = false;
% Attach the horizontal sections to the toolbar
toolbarWidget1.Section = [
section1
section2
];
% Assign a callback
toolbarWidget1.ButtonPushedFcn = @(h,e)disp(e)
toolbarWidget1 =
Toolbar with properties: ButtonPushedFcn: @(h,e)disp(e) @@ -195,7 +195,7 @@ Units: 'pixels' Show all properties -

Toolbar

Vertical sections

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 375 130]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
toolbarWidget2 = wt.Toolbar(gridObj);
% Create a horizontal section with vertical subsection
section3 = wt.toolbar.HorizontalSection();
section3.Title = "MIXED HORIZONTAL / VERTICAL";
section3.addButton("left_24.png","");
section3.addButton("right_24.png","");
% Create the vertical subsection
section3v1 = section3.addVerticalSection();
section3v1.addButton("play_24.png","Play");
section3v1.addButton("stop_24.png","Stop");
% Add a manual component to the vertical section
sliderObj = uislider("Parent",[]);
section3v1.Component(end+1) = sliderObj;
% Attach a callback to the slider
% This is required for manually adding components
sliderObj.ValueChangedFcn = @(h,e)disp(e);
% Create more horizontal items
section3.addButton("pause_24.png","Pause");
% Add another basic section
section4 = wt.toolbar.HorizontalSection();
section4.Title = "HELP";
section4.addButton('help_24.png','Help');
% Attach the horizontal sections to the toolbar
toolbarWidget2.Section = [
section3
section4
];
% Assign a callback
toolbarWidget2.ButtonPushedFcn = @(h,e)disp(e)
toolbarWidget2 =
Toolbar with properties: +

Toolbar

Vertical sections

% Create a figure with a grid layout
figObj = uifigure("Position",[100 100 375 130]);
gridObj = uigridlayout(figObj,[1 1],"BackgroundColor",[.6 .8 1]);
% Create the widget
toolbarWidget2 = wt.Toolbar(gridObj);
% Create a horizontal section with vertical subsection
section3 = wt.toolbar.HorizontalSection();
section3.Title = "MIXED HORIZONTAL / VERTICAL";
section3.addButton("left_24.png","");
section3.addButton("right_24.png","");
% Create the vertical subsection
section3v1 = section3.addVerticalSection();
section3v1.addButton("play_24.png","Play");
section3v1.addButton("stop_24.png","Stop");
% Add a manual component to the vertical section
sliderObj = uislider("Parent",[]);
section3v1.Component(end+1) = sliderObj;
% Attach a callback to the slider
% This is required for manually adding components
sliderObj.ValueChangedFcn = @(h,e)disp(e);
% Create more horizontal items
section3.addButton("pause_24.png","Pause");
% Add another basic section
section4 = wt.toolbar.HorizontalSection();
section4.Title = "HELP";
section4.addButton('help_24.png','Help');
% Attach the horizontal sections to the toolbar
toolbarWidget2.Section = [
section3
section4
];
% Assign a callback
toolbarWidget2.ButtonPushedFcn = @(h,e)disp(e)
toolbarWidget2 =
Toolbar with properties: Section: [2×1 HorizontalSection] DividerColor: [0.5000 0.5000 0.5000] @@ -208,7 +208,7 @@