diff --git a/.github/workflows/wt-ci.yml b/.github/workflows/git-ci.yml similarity index 90% rename from .github/workflows/wt-ci.yml rename to .github/workflows/git-ci.yml index 54b60a04..729b456c 100644 --- a/.github/workflows/wt-ci.yml +++ b/.github/workflows/git-ci.yml @@ -1,15 +1,9 @@ name: Widgets Toolbox Continuous Integration # Controls when the workflow will run. -on: - # Triggers the workflow on push or pull request events, but only for the master branch. - push: - branches: [master] - pull_request: - branches: [master] - +on: [push, workflow_dispatch] + # Triggers the workflow on push or pull request events # This allows the workflow run to be run manually from the Actions tab in GitHub. - workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel. jobs: @@ -20,7 +14,7 @@ jobs: # Set up the job strategy matrix to define the different job configurations. matrix: # List of platforms on which to run the tests. - platform: [ubuntu-latest, windows-latest] + platform: [windows-latest] # List of MATLAB releases over which to run the tests. matlab-version: [R2023b, R2024a, R2024b] diff --git a/deploy/wtDeployVersion.txt b/deploy/wtDeployVersion.txt index 9183195a..197c4d5c 100644 --- a/deploy/wtDeployVersion.txt +++ b/deploy/wtDeployVersion.txt @@ -1 +1 @@ -2.4.0 \ No newline at end of file +2.4.0 diff --git a/deploy/wtPackageRelease.m b/deploy/wtPackageRelease.m index 6074da1c..beecfb36 100644 --- a/deploy/wtPackageRelease.m +++ b/deploy/wtPackageRelease.m @@ -3,18 +3,19 @@ % Copyright 2025 The MathWorks, Inc. +%% Increment Version Number? + +% Increment the last part of the version number in wtDeployVersion.txt for +% the next release +% wt.deploy.incrementVersionNumber(); + + %% Get paths % Project root proj = currentProject; projectRoot = proj.RootFolder; -% docInputPath = fullfile(projectRoot,"doc_input","mlx"); -% docOutputPath = fullfile(projectRoot,"widgets","doc"); - -% docInputPath = fullfile(projectRoot,"widgets","doc","mlx"); -% docOutputPath = fullfile(projectRoot,"widgets","doc","html"); - docInputPath = fullfile(projectRoot,"widgets","doc"); docOutputPath = fullfile(projectRoot,"widgets","doc"); @@ -26,13 +27,13 @@ %% Run unit tests -% [testSuite, testResult] = runTestSuite; -% if ~all([testResult.Passed]) -% error("Unit tests failed. Aborting package release."); -% end +[testSuite, testResult] = runTestSuite; +if ~all([testResult.Passed]) + error("Unit tests failed. Aborting package release."); +end -%% Publish doc_input as html +%% Publish doc and examples mlx as html % Find the doc input ".mlx" files docInputInfo = what(docInputPath); @@ -61,6 +62,8 @@ %% Build search database +% Create v3 for/from R2021a - R2021b +% Create v4 for/from R2022a and later builddocsearchdb(docOutputPath) @@ -81,10 +84,3 @@ releaseFolder = fullfile(projectRoot,"release"); winopen(releaseFolder); - - -%% Increment Version Number - -% Increment the last part of the version number in wtDeployVersion.txt for -% the next release -% wt.deploy.incrementVersionNumber(); diff --git a/release/Widgets Toolbox 2.4.0.mltbx b/release/Widgets Toolbox 2.4.0.mltbx new file mode 100644 index 00000000..d5287eb6 Binary files /dev/null and b/release/Widgets Toolbox 2.4.0.mltbx differ diff --git a/resources/project/-NvNmnLN-CTaezFjMPFOnb8gLqY/2X2h1T9D_nYMAg0S6K7ImwYqP2sd.xml b/resources/project/-NvNmnLN-CTaezFjMPFOnb8gLqY/2X2h1T9D_nYMAg0S6K7ImwYqP2sd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/-NvNmnLN-CTaezFjMPFOnb8gLqY/2X2h1T9D_nYMAg0S6K7ImwYqP2sd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/-NvNmnLN-CTaezFjMPFOnb8gLqY/2X2h1T9D_nYMAg0S6K7ImwYqP2sp.xml b/resources/project/-NvNmnLN-CTaezFjMPFOnb8gLqY/2X2h1T9D_nYMAg0S6K7ImwYqP2sp.xml new file mode 100644 index 00000000..99a2324b --- /dev/null +++ b/resources/project/-NvNmnLN-CTaezFjMPFOnb8gLqY/2X2h1T9D_nYMAg0S6K7ImwYqP2sp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/3sw5dI--hQZYelxK9Mom09YHvwAd.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/3sw5dI--hQZYelxK9Mom09YHvwAd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/3sw5dI--hQZYelxK9Mom09YHvwAd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/3sw5dI--hQZYelxK9Mom09YHvwAp.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/3sw5dI--hQZYelxK9Mom09YHvwAp.xml new file mode 100644 index 00000000..f275cfbe --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/3sw5dI--hQZYelxK9Mom09YHvwAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/AUEm4EcuR_z_A_S4Zx3Y9quOfcQd.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/AUEm4EcuR_z_A_S4Zx3Y9quOfcQd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/AUEm4EcuR_z_A_S4Zx3Y9quOfcQd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/AUEm4EcuR_z_A_S4Zx3Y9quOfcQp.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/AUEm4EcuR_z_A_S4Zx3Y9quOfcQp.xml new file mode 100644 index 00000000..b34655ee --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/AUEm4EcuR_z_A_S4Zx3Y9quOfcQp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/PEvaWanHyEMsSZ3At1l38zHZIIMd.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/PEvaWanHyEMsSZ3At1l38zHZIIMd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/PEvaWanHyEMsSZ3At1l38zHZIIMd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/PEvaWanHyEMsSZ3At1l38zHZIIMp.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/PEvaWanHyEMsSZ3At1l38zHZIIMp.xml new file mode 100644 index 00000000..9d2534a2 --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/PEvaWanHyEMsSZ3At1l38zHZIIMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/POF3EozTISoDuz7PAvQFUtnM2bAd.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/POF3EozTISoDuz7PAvQFUtnM2bAd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/POF3EozTISoDuz7PAvQFUtnM2bAd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/POF3EozTISoDuz7PAvQFUtnM2bAp.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/POF3EozTISoDuz7PAvQFUtnM2bAp.xml new file mode 100644 index 00000000..b35f6596 --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/POF3EozTISoDuz7PAvQFUtnM2bAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/iqi52LMlP4L2z3HNvB6oIkA-UOEd.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/iqi52LMlP4L2z3HNvB6oIkA-UOEd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/iqi52LMlP4L2z3HNvB6oIkA-UOEd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/iqi52LMlP4L2z3HNvB6oIkA-UOEp.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/iqi52LMlP4L2z3HNvB6oIkA-UOEp.xml new file mode 100644 index 00000000..14650488 --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/iqi52LMlP4L2z3HNvB6oIkA-UOEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/nsGQhR6faxKjSdhCXeouyoTTNjkd.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/nsGQhR6faxKjSdhCXeouyoTTNjkd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/nsGQhR6faxKjSdhCXeouyoTTNjkd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/nsGQhR6faxKjSdhCXeouyoTTNjkp.xml b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/nsGQhR6faxKjSdhCXeouyoTTNjkp.xml new file mode 100644 index 00000000..01cb34e6 --- /dev/null +++ b/resources/project/2X2h1T9D_nYMAg0S6K7ImwYqP2s/nsGQhR6faxKjSdhCXeouyoTTNjkp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/i4vUgcSlNK8GFhk0ZzD7YU0sDdMd.xml b/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/i4vUgcSlNK8GFhk0ZzD7YU0sDdMd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/i4vUgcSlNK8GFhk0ZzD7YU0sDdMd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/i4vUgcSlNK8GFhk0ZzD7YU0sDdMp.xml b/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/i4vUgcSlNK8GFhk0ZzD7YU0sDdMp.xml new file mode 100644 index 00000000..230db5da --- /dev/null +++ b/resources/project/FVIFMHuse2VWlFgbqX2S_OQzxiU/i4vUgcSlNK8GFhk0ZzD7YU0sDdMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/V76yfcu8kfyIeJKa13j7YbWR_NEd.xml b/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/V76yfcu8kfyIeJKa13j7YbWR_NEd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/V76yfcu8kfyIeJKa13j7YbWR_NEd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/V76yfcu8kfyIeJKa13j7YbWR_NEp.xml b/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/V76yfcu8kfyIeJKa13j7YbWR_NEp.xml new file mode 100644 index 00000000..01cb34e6 --- /dev/null +++ b/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/V76yfcu8kfyIeJKa13j7YbWR_NEp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/p2B2kuKn-TR4inf0L2-zoGElvj0d.xml b/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/p2B2kuKn-TR4inf0L2-zoGElvj0d.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/p2B2kuKn-TR4inf0L2-zoGElvj0d.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/p2B2kuKn-TR4inf0L2-zoGElvj0p.xml b/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/p2B2kuKn-TR4inf0L2-zoGElvj0p.xml new file mode 100644 index 00000000..db2f6dfb --- /dev/null +++ b/resources/project/I-dDyJ1p--Q0ihIJ8NFdLVLQLTU/p2B2kuKn-TR4inf0L2-zoGElvj0p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/1P9lZ3TQ_Wxqwq6HkKXuWRrY8C0d.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/1P9lZ3TQ_Wxqwq6HkKXuWRrY8C0d.xml deleted file mode 100644 index 7a6326b9..00000000 --- a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/1P9lZ3TQ_Wxqwq6HkKXuWRrY8C0d.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/1P9lZ3TQ_Wxqwq6HkKXuWRrY8C0p.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/1P9lZ3TQ_Wxqwq6HkKXuWRrY8C0p.xml deleted file mode 100644 index 2b7243b3..00000000 --- a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/1P9lZ3TQ_Wxqwq6HkKXuWRrY8C0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/a5u8Qi462agCWWagpfqETKKb6i0d.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/a5u8Qi462agCWWagpfqETKKb6i0d.xml deleted file mode 100644 index 7a6326b9..00000000 --- a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/a5u8Qi462agCWWagpfqETKKb6i0d.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/a5u8Qi462agCWWagpfqETKKb6i0p.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/a5u8Qi462agCWWagpfqETKKb6i0p.xml deleted file mode 100644 index 815c8871..00000000 --- a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/a5u8Qi462agCWWagpfqETKKb6i0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/gMc-baj-As5GWjAMCfprUMoq1-kd.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/gMc-baj-As5GWjAMCfprUMoq1-kd.xml deleted file mode 100644 index 7a6326b9..00000000 --- a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/gMc-baj-As5GWjAMCfprUMoq1-kd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/gMc-baj-As5GWjAMCfprUMoq1-kp.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/gMc-baj-As5GWjAMCfprUMoq1-kp.xml deleted file mode 100644 index 4fd0a59f..00000000 --- a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/gMc-baj-As5GWjAMCfprUMoq1-kp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/0bGHnSJuVpCWz5Zvf6kftgyPKZUd.xml b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/0bGHnSJuVpCWz5Zvf6kftgyPKZUd.xml deleted file mode 100644 index 7a6326b9..00000000 --- a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/0bGHnSJuVpCWz5Zvf6kftgyPKZUd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/0bGHnSJuVpCWz5Zvf6kftgyPKZUp.xml b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/0bGHnSJuVpCWz5Zvf6kftgyPKZUp.xml deleted file mode 100644 index bf59a24a..00000000 --- a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/0bGHnSJuVpCWz5Zvf6kftgyPKZUp.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/2jbfV_s44Hws0oR4FWOA3ltl3O0d.xml b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/2jbfV_s44Hws0oR4FWOA3ltl3O0d.xml deleted file mode 100644 index 7a6326b9..00000000 --- a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/2jbfV_s44Hws0oR4FWOA3ltl3O0d.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/2jbfV_s44Hws0oR4FWOA3ltl3O0p.xml b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/2jbfV_s44Hws0oR4FWOA3ltl3O0p.xml deleted file mode 100644 index f8018acd..00000000 --- a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/2jbfV_s44Hws0oR4FWOA3ltl3O0p.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/0kK47cOHwsW7a1GaaqpQDigXMNUd.xml b/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/0kK47cOHwsW7a1GaaqpQDigXMNUd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/0kK47cOHwsW7a1GaaqpQDigXMNUd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/0kK47cOHwsW7a1GaaqpQDigXMNUp.xml b/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/0kK47cOHwsW7a1GaaqpQDigXMNUp.xml new file mode 100644 index 00000000..01cb34e6 --- /dev/null +++ b/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/0kK47cOHwsW7a1GaaqpQDigXMNUp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/RZl_i_GDnhyBwbhbVbhurwxgkbMd.xml b/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/RZl_i_GDnhyBwbhbVbhurwxgkbMd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/RZl_i_GDnhyBwbhbVbhurwxgkbMd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/RZl_i_GDnhyBwbhbVbhurwxgkbMp.xml b/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/RZl_i_GDnhyBwbhbVbhurwxgkbMp.xml new file mode 100644 index 00000000..1ffd2c2d --- /dev/null +++ b/resources/project/p2B2kuKn-TR4inf0L2-zoGElvj0/RZl_i_GDnhyBwbhbVbhurwxgkbMp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/I-dDyJ1p--Q0ihIJ8NFdLVLQLTUd.xml b/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/I-dDyJ1p--Q0ihIJ8NFdLVLQLTUd.xml new file mode 100644 index 00000000..4356a6ae --- /dev/null +++ b/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/I-dDyJ1p--Q0ihIJ8NFdLVLQLTUd.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/I-dDyJ1p--Q0ihIJ8NFdLVLQLTUp.xml b/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/I-dDyJ1p--Q0ihIJ8NFdLVLQLTUp.xml new file mode 100644 index 00000000..a9c1803b --- /dev/null +++ b/resources/project/qaw0eS1zuuY1ar9TdPn1GMfrjbQ/I-dDyJ1p--Q0ihIJ8NFdLVLQLTUp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/test/+wt/+test/ProjectIntegrityChecks.m b/test/+wt/+test/ProjectIntegrityChecks.m index deb0c526..234d418d 100644 --- a/test/+wt/+test/ProjectIntegrityChecks.m +++ b/test/+wt/+test/ProjectIntegrityChecks.m @@ -1,6 +1,8 @@ classdef ProjectIntegrityChecks < matlab.unittest.TestCase % Implements a unit test that runs the Project Integrity Checks + % Copyright 2025 The MathWorks Inc. + methods(Test) function runProjectChecks(testCase) diff --git a/test/+wt/+test/RowEntriesTable.m b/test/+wt/+test/RowEntriesTable.m index 79cd1f51..12ec8d0d 100644 --- a/test/+wt/+test/RowEntriesTable.m +++ b/test/+wt/+test/RowEntriesTable.m @@ -219,6 +219,9 @@ function testEdits(testCase) % Verify the data match testCase.verifyDataProperty(initData); + % Ensure rendering is finished + drawnow + % Type new values testCase.type(reTable,[6 1],"Pineapple") testCase.type(reTable,[6 2],"1234") diff --git a/widgets/+wt/+abstract/BaseMultiAxesTimeChart.m b/widgets/+wt/+abstract/BaseMultiAxesTimeChart.m deleted file mode 100644 index c2758a4b..00000000 --- a/widgets/+wt/+abstract/BaseMultiAxesTimeChart.m +++ /dev/null @@ -1,234 +0,0 @@ -classdef BaseMultiAxesTimeChart< ... - matlab.graphics.chartcontainer.ChartContainer & ... - wt.mixin.BoundMultiTimeAxes & ... - wt.mixin.BoundTitleText - % Base class for a time chart with multi axes - - % Copyright 2022-2025 The MathWorks Inc. - - - - %% Public Properties - properties (AbortSet, Access = public) - - % How many axes to display - NumAxes (1,1) double {mustBePositive,mustBeInteger} = 3 - - % Overall group title -% GroupTitle (1,1) string - - end %properties - - % Accessors - methods - - function set.NumAxes(obj,value) - obj.NumAxes = value; - obj.createAxes(); - end - - end %methods - - - %% Internal Properties - properties (Transient, NonCopyable, Hidden, SetAccess = protected) - - % TiledLayout for axes - TiledLayout matlab.graphics.layout.TiledChartLayout {mustBeScalarOrEmpty} - - % Axes to display the signal - Axes (:,1) matlab.graphics.axis.Axes - - % Legend of each axes - Legend (:,1) matlab.graphics.illustration.Legend - - end %properties - - - properties (Transient, NonCopyable, UsedInUpdate=true, Access = private) - - % Internal flag to trigger an update call - Dirty_ (1,1) logical = false - - end %properties - - - properties (Transient, NonCopyable, Access = private) - - % State of axes layout - AxesLayoutDirty (1,1) logical = true - - end %properties - - - %% Protected methods - methods (Access = protected) - - function setup(obj) - % Create the underlying components - - % Configure Layout - obj.TiledLayout = getLayout(obj); - obj.TiledLayout.Padding = "compact"; - obj.TiledLayout.TileSpacing = "compact"; - obj.TiledLayout.GridSize = [obj.NumAxes 1]; - - % Create the initial axes - obj.createAxes(); - -% obj.TitleInterpreter = "none"; % default to off - - end %function - - - function createAxes(obj) - % Triggered on initial setup or NumAxes changed - - % How many axes to make? - newNumAxes = obj.NumAxes; - - % Get existing valid axes - oldAxes = obj.Axes; - isInvalid = ~isvalid(oldAxes); - oldAxes(isInvalid) = []; - oldNumAxes = numel(oldAxes); - - % Remove old axes from the tiled layout - set(oldAxes, "Parent", []); - - % Update the TiledLayout size - obj.TiledLayout.GridSize = [newNumAxes 1]; - - % Get existing legends - oldLegend = obj.Legend; - isInvalid = ~isvalid(oldLegend); - oldLegend(isInvalid) = []; - oldNumLegend = numel(oldLegend); - assert(oldNumAxes == oldNumLegend, ... - "Internal Error. Number of legends did not match number of axes.") - - % Do we need to add or delete? - if newNumAxes < oldNumAxes - % Need to delete extra axes - - % Prepare new lists - newAxes = oldAxes(1:newNumAxes); - newLegend = oldLegend(1:newNumAxes); - - % Delete old ones - removeAxes = oldAxes((newNumAxes+1) : end); - delete(removeAxes) - - % Put new axes back in the tiled layout - set(newAxes, "Parent", obj.TiledLayout); - % for axIdx = 1:newNumAxes - % newAxes - - else - % Need to add new axes - - % Put old axes back in the tiled layout - set(oldAxes, "Parent", obj.TiledLayout); - - % Preallocate - newAxes = gobjects(1, newNumAxes); - newAxes(1:oldNumAxes) = oldAxes; - - newLegend = gobjects(1, newNumAxes); - newLegend(1:oldNumAxes) = oldLegend; - - % Get modes - if oldNumAxes > 0 - currentInterpreter = obj.Interpreter; - currentShowLegend = obj.ShowLegend; - else - currentInterpreter = "none"; - currentShowLegend = false; - end - - % Add more axes - for axIdx = (oldNumAxes+1) : newNumAxes - - ax = nexttile(obj.TiledLayout); - ax.NextPlot = "add"; - ax.XAxis = matlab.graphics.axis.decorator.DurationRuler(); - ax.XAxis.TickLabelInterpreter = currentInterpreter; - ax.YAxis.TickLabelInterpreter = currentInterpreter; - ax.Title.Interpreter = currentInterpreter; - ax.XLabel.Interpreter = currentInterpreter; - ax.YLabel.Interpreter = currentInterpreter; - ax.ZLimMode = "manual"; - - % Create the legend - lgnd = legend(ax,... - "Location","northwest",... - "Interpreter",currentInterpreter,... - "Visible",currentShowLegend); - - newAxes(axIdx) = ax; - newLegend(axIdx) = lgnd; - - end %for - - end %if - - % Were there multiple axes? - if newNumAxes > 1 - - % Remove X tick labels for all but last axes - set(newAxes(1:end-1), "XTickLabel", {}); - - % Link all the axes in X - try - linkaxes(newAxes, 'x'); - catch err - warning("wt:abstract:BaseMultiAxesTimeChart:LinkAxesError",... - "Unable to link axes: %s", err.message); - end - - end %if - - % Store new objects - obj.Axes = newAxes; - obj.Legend = newLegend; - - % Set bound properties - obj.BoundAxes = obj.Axes; - obj.BoundLegend = obj.Legend; - - end %function - - - function update(obj) - % Update the underlying components - - disp('updated'); - - end %function - - - function requestUpdate(obj) - % Request update method to run - - % Trigger set of a UsedInUpdate property to request update - % during next drawnow. (for optimal efficiency) - obj.Dirty_ = ~obj.Dirty_; - - end %function - - end %methods - - - %% Debugging Methods - methods - - function forceUpdateChart(obj) - % Forces update to run (For debugging only!) - - obj.update(); - - end %function - - end %methods - -end %classdef diff --git a/widgets/+wt/+abstract/BaseSingleAxesChart.m b/widgets/+wt/+abstract/BaseSingleAxesChart.m deleted file mode 100644 index 07daf2d3..00000000 --- a/widgets/+wt/+abstract/BaseSingleAxesChart.m +++ /dev/null @@ -1,123 +0,0 @@ -classdef BaseSingleAxesChart < ... - matlab.graphics.chartcontainer.ChartContainer & ... - wt.mixin.BoundSingleTimeAxes - % Base class for a time chart with single axes - - % Copyright 2022-2025 The MathWorks Inc. - - - %% Internal Properties - properties (Transient, NonCopyable, Hidden, SetAccess = protected) - - % TiledLayout for axes - TiledLayout matlab.graphics.layout.TiledChartLayout {mustBeScalarOrEmpty} - - % Axes to display the signal - Axes matlab.graphics.axis.Axes {mustBeScalarOrEmpty} - - % Legend of each axes - Legend matlab.graphics.illustration.Legend {mustBeScalarOrEmpty} - - end %properties - - - properties (Transient, NonCopyable, UsedInUpdate=true, Access = private) - - % Internal flag to trigger an update call - Dirty_ (1,1) logical = false - - end %properties - - - %% Debugging Methods - methods - - function forceUpdateChart(obj) - % Forces update to run (For debugging only!) - - obj.update(); - - end %function - - end %methods - - - %% Protected methods - methods (Access = protected) - - function setup(obj) - % Create the underlying components - - % Configure Layout - obj.TiledLayout = getLayout(obj); - obj.TiledLayout.Padding = "compact"; - obj.TiledLayout.TileSpacing = "compact"; - obj.TiledLayout.GridSize = [1 1]; - - % Create the axes - ax = nexttile(obj.TiledLayout); - ax.XAxis = matlab.graphics.axis.decorator.DurationRuler(); - ax.XAxis.TickLabelInterpreter = "none"; - ax.YAxis.TickLabelInterpreter = "none"; - ax.Title.Interpreter = "none"; - ax.XLabel.Interpreter = "none"; - ax.YLabel.Interpreter = "none"; - ax.ZLimMode = "manual"; - - % Create the legend - lgnd = legend(ax,'Location',"northwest"); - lgnd.Interpreter = "none"; - lgnd.Visible = "off"; - - % Store new objects - obj.Axes = ax; - obj.Legend = lgnd; - - % Bind components to their style mixins - obj.BoundAxes = ax; - - end %function - - - function update(~) - % Update the underlying components - - - end %function - - - function requestUpdate(obj) - % Request update method to run - - % Trigger set of a UsedInUpdate property to request update - % during next drawnow. (for optimal efficiency) - obj.Dirty_ = ~obj.Dirty_; - - end %function - - - - function groups = getPropertyGroups(obj) - % Customize how the properties are displayed - - % Ignore most superclass properties for default display - persistent superProps - if isempty(superProps) - superProps = properties('matlab.graphics.chartcontainer.ChartContainer'); - end - - % Get the relevant properties (ignore Superclass properties) - allProps = properties(obj); - propNames = setdiff(allProps, superProps, 'stable'); - - % Define the property gorups - groups = [ - matlab.mixin.util.PropertyGroup(propNames) - matlab.mixin.util.PropertyGroup(["Position","Units"]) - ]; - - end %function - - end %methods - -end %classdef \ No newline at end of file diff --git a/widgets/+wt/+dialog/Login.m b/widgets/+wt/+dialog/Login.m index 01dcfbaf..b920550b 100644 --- a/widgets/+wt/+dialog/Login.m +++ b/widgets/+wt/+dialog/Login.m @@ -1,6 +1,8 @@ classdef Login < wt.abstract.BaseInternalDialog % Implements a simple login dialog + % Copyright 2025 The MathWorks Inc. + %% Internal Properties properties (Transient, NonCopyable, Hidden, SetAccess = private) diff --git a/widgets/+wt/+mixin/BoundMultiTimeAxes.m b/widgets/+wt/+mixin/BoundMultiTimeAxes.m deleted file mode 100644 index 039d8608..00000000 --- a/widgets/+wt/+mixin/BoundMultiTimeAxes.m +++ /dev/null @@ -1,372 +0,0 @@ -classdef BoundMultiTimeAxes < handle - % Mixin that binds axes properties for a chartcontainer subclass - - %% Protected properties - - properties (Access = protected) - - % The internal axes object to bind to - BoundAxes (:,1) - - % The axes legends to bind to - BoundLegend (:,1) - - end %properties - - - %% Public Dependent Properties (bound to contents) - properties (AbortSet, Dependent) - - % Font Color - Color - - % Grid color - GridColor - - % X-Axis Label - XLabel (1,1) string - - % Y-Axis Label - YLabel (:,1) string - - % Axes Y-Limits - YLim (:,2) cell - - % Axes Y-Limit Mode - YLimMode (1,1) string - - % Font name - FontName - - % Font size - FontSize - - % Font weight - FontWeight - - % Interpreter - Interpreter - - % Axes Labels Font name - LabelFontName - - % Axes Labels Font size in points - LabelFontSize - - % Axes Labels Font weight (normal/bold) - LabelFontWeight - - % Show grid on each axes? - ShowGrid - - % Show legend on each axes? - ShowLegend - - end %properties - - - %% Property Accessors - methods - - function value = get.Color(obj) - value = obj.getFirstAxesPropertyValue("Color"); - end - function set.Color(obj, value) - obj.setAxesPropertyToSingleValue("Color", value); - obj.setLegendPropertyToSingleValue("Color", value); - end - - function value = get.GridColor(obj) - value = obj.getFirstAxesPropertyValue("GridColor"); - end - function set.GridColor(obj, value) - obj.setAxesPropertyToSingleValue("GridColor", value); - end - - function value = get.XLabel(obj) - ax = getValidAxes(obj); - if isempty(ax) - value = ""; - else - value = ax(end).XLabel.String; - end - end - function set.XLabel(obj, value) - ax = getValidAxes(obj); - if ~isempty(ax) - ax(end).XLabel.String = value; - end - end - - function value = get.YLabel(obj) - [ax, isValid] = getValidAxes(obj); - value = strings(size(isValid)); - value(isValid) = [string({ax(isValid.YLabel.String)})]; - end - function set.YLabel(obj, value) - [ax, isValid] = getValidAxes(obj); - numAxes = numel(isValid); - value = wt.utility.resizeVector(value, numAxes, ""); - valueV = value(isValid); - for idx = 1:numel(ax) - ax(idx).YLabel.String = valueV(idx); - end - end - - function value = get.FontName(obj) - value = obj.getFirstAxesPropertyValue("FontName"); - end - function set.FontName(obj, value) - obj.setAxesPropertyToSingleValue("FontName", value); - end - - function value = get.FontSize(obj) - value = obj.getFirstAxesPropertyValue("FontSize"); - end - function set.FontSize(obj, value) - obj.setAxesPropertyToSingleValue("FontSize", value); - end - - function value = get.FontWeight(obj) - value = obj.getFirstAxesPropertyValue("FontWeight"); - end - function set.FontWeight(obj, value) - obj.setAxesPropertyToSingleValue("FontWeight", value); - end - - function value = get.Interpreter(obj) - value = obj.getFirstAxesComponentValue("YLabel","Interpreter"); - end - function set.Interpreter(obj, value) - obj.setAxesComponentPropertyToSingleValue("XLabel", "Interpreter", value); - obj.setAxesComponentPropertyToSingleValue("YLabel", "Interpreter", value); - obj.setAxesComponentPropertyToSingleValue("XAxis", "TickLabelInterpreter", value); - obj.setAxesComponentPropertyToSingleValue("YAxis", "TickLabelInterpreter", value); - end - - function value = get.LabelFontName(obj) - value = obj.getFirstAxesComponentValue("YLabel","FontName"); - end - function set.LabelFontName(obj, value) - obj.setAxesComponentPropertyToSingleValue("XLabel", "FontName", value); - obj.setAxesComponentPropertyToSingleValue("YLabel", "FontName", value); - end - - function value = get.LabelFontSize(obj) - value = obj.getFirstAxesComponentValue("YLabel","FontSize"); - end - function set.LabelFontSize(obj, value) - obj.setAxesComponentPropertyToSingleValue("XLabel", "FontSize", value); - obj.setAxesComponentPropertyToSingleValue("YLabel", "FontSize", value); - end - - function value = get.LabelFontWeight(obj) - value = obj.getFirstAxesComponentValue("YLabel","FontWeight"); - end - function set.LabelFontWeight(obj, value) - obj.setAxesComponentPropertyToSingleValue("XLabel", "FontWeight", value); - obj.setAxesComponentPropertyToSingleValue("YLabel", "FontWeight", value); - end - - function value = get.ShowGrid(obj) - value = obj.getFirstAxesPropertyValue("YGrid"); - end - function set.ShowGrid(obj, value) - obj.setAxesPropertyToSingleValue("XGrid", value); - obj.setAxesPropertyToSingleValue("YGrid", value); - end - - function value = get.ShowLegend(obj) - value = obj.getFirstLegendPropertyValue("Visible"); - end - function set.ShowLegend(obj, value) - obj.setLegendPropertyToSingleValue("Visible", value); - end - - end %methods - - - %% Private methods - methods (Access = private) - - function [ax, isValid] = getValidAxes(obj) - ax = obj.BoundAxes; - isValid = isvalid(ax); - ax = ax(isValid); - end - - function [lgd, isValid] = getValidLegend(obj) - lgd = obj.BoundLegend; - isValid = isvalid(lgd); - lgd = lgd(isValid); - end - - function value = getFirstAxesPropertyValue(obj, propName) - - % Store a default object in case no axess exist yet - persistent defaultAxes - if isempty(defaultAxes) - defaultAxes = matlab.graphics.axis.Axes("Parent",[]); - end - - % Get the first valid axes - ax = obj.getValidAxes(); - if isempty(ax) - ax = defaultAxes; - end - - % Get the property requested - value = ax(1).(propName); - - end %function - - - function value = getFirstLegendPropertyValue(obj, propName) - - % Store a default object in case no axess exist yet - persistent defaultLegend - if isempty(defaultLegend) - defaultLegend = matlab.graphics.illustration.Legend(... - "Parent",[],"Visible","off"); - end - - % Get the first valid legend - lgd = obj.getValidLegend(); - if isempty(lgd) - lgd = defaultLegend; - end - - % Get the property requested - value = lgd(1).(propName); - - end %function - - - function setAxesPropertyToSingleValue(obj, propName, value) - - % Get valid axes objects - ax = obj.getValidAxes(); - - % Set the property requested - wt.utility.fastSet(ax, propName, value) - - end %function - - - function setLegendPropertyToSingleValue(obj, propName, value) - - % Get valid legend objects - lgd = obj.getValidLegend(); - - % Set the property requested - wt.utility.fastSet(lgd, propName, value) - - end %function - - - function value = getFirstAxesComponentValue(obj, compName, propName) - - % Store a default object in case no axess exist yet - persistent defaultAxes - if isempty(defaultAxes) - defaultAxes = matlab.graphics.axis.Axes("Parent",[]); - end - - % Get the first valid axes - ax = obj.getValidAxes(); - if isempty(ax) - ax = defaultAxes; - end - - % Get the property requested - value = ax(1).(compName).(propName); - - end %function - - - % function setComponentProperty(~, allComp, propName, value) - % - % % Get valid objects - % allComp(~isvalid(allComp)) = []; - % - % % Set the property requested - % wt.utility.fastSet(allComp, propName, value) - % - % end %function - - - function setAxesComponentPropertyToSingleValue(obj, compName, propName, value) - - % Get valid objects - ax = getValidAxes(obj); - - % Get components - allComp = [ax.(compName)]; - - % Set the property requested - wt.utility.fastSet(allComp, propName, value) - - end %function - - - end %methods - - - %% Reserved for future use - % These may be added in the future. Defining them here so that - % subclasses should not use these reserved properties yet. - properties (Transient, GetAccess = protected, SetAccess = immutable) - - Color_I - ColorMode - - GridColor_I - GridColorMode - - - FontName_I - FontNameMode - - FontSize_I - FontSizeMode - - FontWeight_I - FontWeightMode - - FontAngle - FontAngle_I - FontAngleMode - - FontUnits - FontUnits_I - FontUnitsMode - - FontSmoothing - FontSmoothing_I - FontSmoothingMode - - - LabelFontName_I - LabelFontNameMode - - LabelFontSize_I - LabelFontSizeMode - - LabelFontWeight_I - LabelFontWeightMode - - LabelFontAngle - LabelFontAngle_I - LabelFontAngleMode - - LabelFontUnits - LabelFontUnits_I - LabelFontUnitsMode - - LabelFontSmoothing - LabelFontSmoothing_I - LabelFontSmoothingMode - - end %properties - -end %classdef \ No newline at end of file diff --git a/widgets/+wt/+mixin/BoundSingleTimeAxes.m b/widgets/+wt/+mixin/BoundSingleTimeAxes.m deleted file mode 100644 index 3cc14d52..00000000 --- a/widgets/+wt/+mixin/BoundSingleTimeAxes.m +++ /dev/null @@ -1,410 +0,0 @@ -classdef BoundSingleTimeAxes < handle - % Mixin that binds axes properties for a chartcontainer subclass - - %% Protected properties - - properties (Transient, NonCopyable, Access = protected) - - % The internal axes object to bind to - BoundAxes matlab.graphics.axis.Axes {mustBeScalarOrEmpty} - - end %properties - - - %% Public Dependent Properties (bound to contents) - properties (AbortSet, Dependent) - - % Axes Y-Limits - YLim (1,2) - - % Axes Y-Limit Mode - YLimMode (1,1) string - - % Font Color - Color - - % Grid color - GridColor - - % Font name - FontName - - % Font size - FontSize - - % Font weight - FontWeight - - % X-Axis Label - XLabel (1,1) string - - % Y-Axis Label - YLabel (1,1) string - - % Axes Labels Font name - LabelFontName - - % Axes Labels Font size in points - LabelFontSize - - % Axes Labels Font weight (normal/bold) - LabelFontWeight - - % Axes Title Text - Title (1,1) string - - % Title Font Color - TitleColor - - % Title Font name - TitleFontName - - % Title Font size in points - TitleFontSize - - % Title Font weight (normal/bold) - TitleFontWeight - - % Interpreter - Interpreter - - % Show grid on each axes? - ShowGrid - - % Show legend on each axes? - ShowLegend - - end %properties - - - %% Property Accessors - methods - - function value = get.YLim(obj) - value = obj.getFirstAxesPropertyValue("YLim"); - end - function set.YLim(obj, value) - obj.setAxesPropertyToSingleValue("YLim", value); - end - - function value = get.YLimMode(obj) - value = obj.getFirstAxesPropertyValue("YLimMode"); - end - function set.YLimMode(obj, value) - obj.setAxesPropertyToSingleValue("YLimMode", value); - end - - function value = get.Color(obj) - value = obj.getFirstAxesPropertyValue("Color"); - end - function set.Color(obj, value) - obj.setAxesPropertyToSingleValue("Color", value); - obj.setLegendPropertyToSingleValue("Color", value); - end - - function value = get.GridColor(obj) - value = obj.getFirstAxesPropertyValue("GridColor"); - end - function set.GridColor(obj, value) - obj.setAxesPropertyToSingleValue("GridColor", value); - end - - function value = get.XLabel(obj) - value = obj.getFirstAxesComponentValue("XLabel", "String"); - end - function set.XLabel(obj, value) - obj.setAxesComponentPropertyToSingleValue("XLabel", "String", value); - end - - function value = get.YLabel(obj) - value = obj.getFirstAxesComponentValue("YLabel", "String"); - end - function set.YLabel(obj, value) - obj.setAxesComponentPropertyToSingleValue("YLabel", "String", value); - end - - function value = get.FontName(obj) - value = obj.getFirstAxesPropertyValue("FontName"); - end - function set.FontName(obj, value) - obj.setAxesPropertyToSingleValue("FontName", value); - end - - function value = get.FontSize(obj) - value = obj.getFirstAxesPropertyValue("FontSize"); - end - function set.FontSize(obj, value) - obj.setAxesPropertyToSingleValue("FontSize", value); - end - - function value = get.FontWeight(obj) - value = obj.getFirstAxesPropertyValue("FontWeight"); - end - function set.FontWeight(obj, value) - obj.setAxesPropertyToSingleValue("FontWeight", value); - end - - function value = get.Interpreter(obj) - value = obj.getFirstAxesComponentValue("YLabel","Interpreter"); - end - function set.Interpreter(obj, value) - obj.setAxesComponentPropertyToSingleValue("XLabel", "Interpreter", value); - obj.setAxesComponentPropertyToSingleValue("YLabel", "Interpreter", value); - obj.setAxesComponentPropertyToSingleValue("XAxis", "TickLabelInterpreter", value); - obj.setAxesComponentPropertyToSingleValue("YAxis", "TickLabelInterpreter", value); - end - - function value = get.LabelFontName(obj) - value = obj.getFirstAxesComponentValue("YLabel","FontName"); - end - function set.LabelFontName(obj, value) - obj.setAxesComponentPropertyToSingleValue("XLabel", "FontName", value); - obj.setAxesComponentPropertyToSingleValue("YLabel", "FontName", value); - end - - function value = get.LabelFontSize(obj) - value = obj.getFirstAxesComponentValue("YLabel","FontSize"); - end - function set.LabelFontSize(obj, value) - obj.setAxesComponentPropertyToSingleValue("XLabel", "FontSize", value); - obj.setAxesComponentPropertyToSingleValue("YLabel", "FontSize", value); - end - - function value = get.LabelFontWeight(obj) - value = obj.getFirstAxesComponentValue("YLabel","FontWeight"); - end - function set.LabelFontWeight(obj, value) - obj.setAxesComponentPropertyToSingleValue("XLabel", "FontWeight", value); - obj.setAxesComponentPropertyToSingleValue("YLabel", "FontWeight", value); - end - - function value = get.Title(obj) - value = obj.getFirstAxesComponentValue("Title", "String"); - end - function set.Title(obj, value) - obj.setAxesComponentPropertyToSingleValue("Title", "String", value); - end - - function value = get.TitleFontName(obj) - value = obj.getFirstAxesComponentValue("Title","FontName"); - end - function set.TitleFontName(obj, value) - obj.setAxesComponentPropertyToSingleValue("Title", "FontName", value); - end - - function value = get.TitleFontSize(obj) - value = obj.getFirstAxesComponentValue("Title","FontSize"); - end - function set.TitleFontSize(obj, value) - obj.setAxesComponentPropertyToSingleValue("Title", "FontSize", value); - end - - function value = get.TitleFontWeight(obj) - value = obj.getFirstAxesComponentValue("Title","FontWeight"); - end - function set.TitleFontWeight(obj, value) - obj.setAxesComponentPropertyToSingleValue("Title", "FontWeight", value); - end - - function value = get.ShowGrid(obj) - value = obj.getFirstAxesPropertyValue("YGrid"); - end - function set.ShowGrid(obj, value) - obj.setAxesPropertyToSingleValue("XGrid", value); - obj.setAxesPropertyToSingleValue("YGrid", value); - end - - function value = get.ShowLegend(obj) - value = obj.getFirstLegendPropertyValue("Visible"); - end - function set.ShowLegend(obj, value) - obj.setLegendPropertyToSingleValue("Visible", value); - end - - end %methods - - - %% Private methods - methods (Access = private) - - function [ax, isValid] = getValidAxes(obj) - ax = obj.BoundAxes; - isValid = isvalid(ax); - ax = ax(isValid); - end - - function [lgd, isValid] = getValidLegend(obj) - [ax, isValid] = getValidAxes(obj); - if isempty(ax) || ~isValid || isempty(ax.Legend) - lgd = matlab.graphics.illustration.Legend.empty(0); - isValid = logical.empty(0); - else - lgd = ax.Legend; - isValid = true; - end - end - - function value = getFirstAxesPropertyValue(obj, propName) - - % Store a default object in case no axess exist yet - persistent defaultAxes - if isempty(defaultAxes) - defaultAxes = matlab.graphics.axis.Axes("Parent",[]); - end - - % Get the first valid axes - ax = getValidAxes(obj); - if isempty(ax) - firstAxes = defaultAxes; - else - firstAxes = ax(1); - end - - % Get the property requested - value = firstAxes.(propName); - - end %function - - - function value = getFirstLegendPropertyValue(obj, propName) - - % Store a default object in case no axess exist yet - persistent defaultLegend - if isempty(defaultLegend) - defaultLegend = matlab.graphics.illustration.Legend(... - "Parent",[],"Visible","off"); - end - - % Get the first valid legend - lgd = obj.getValidLegend(); - if isempty(lgd) - lgd = defaultLegend; - end - - % Get the property requested - value = lgd(1).(propName); - - end %function - - - function setAxesPropertyToSingleValue(obj, propName, value) - - % Get valid axes objects - ax = obj.getValidAxes(); - - % Set the property requested - wt.utility.fastSet(ax, propName, value) - - end %function - - - function setLegendPropertyToSingleValue(obj, propName, value) - - % Get valid legend objects - lgd = obj.getValidLegend(); - - % Set the property requested - wt.utility.fastSet(lgd, propName, value) - - end %function - - - function value = getFirstAxesComponentValue(obj, compName, propName) - - % Store a default object in case no axess exist yet - persistent defaultAxes - if isempty(defaultAxes) - defaultAxes = matlab.graphics.axis.Axes("Parent",[]); - end - - % Get the first valid axes - ax = obj.getValidAxes(); - if isempty(ax) - ax = defaultAxes; - end - - % Get the property requested - value = ax(1).(compName).(propName); - - end %function - - - function setAxesComponentPropertyToSingleValue(obj, compName, propName, value) - - % Get valid objects - ax = getValidAxes(obj); - - % Can't be empty - if isempty(ax) - warning("Axes is empty"); - end - - % Get components - allComp = [ax.(compName)]; - - % Set the property requested - wt.utility.fastSet(allComp, propName, value) - - end %function - - end %methods - - - %% Reserved for future use - % These may be added in the future. Defining them here so that - % subclasses should not use these reserved properties yet. - properties (Transient, GetAccess = protected, SetAccess = immutable) - - Color_I - ColorMode - - GridColor_I - GridColorMode - - - FontName_I - FontNameMode - - FontSize_I - FontSizeMode - - FontWeight_I - FontWeightMode - - FontAngle - FontAngle_I - FontAngleMode - - FontUnits - FontUnits_I - FontUnitsMode - - FontSmoothing - FontSmoothing_I - FontSmoothingMode - - - LabelFontName_I - LabelFontNameMode - - LabelFontSize_I - LabelFontSizeMode - - LabelFontWeight_I - LabelFontWeightMode - - LabelFontAngle - LabelFontAngle_I - LabelFontAngleMode - - LabelFontUnits - LabelFontUnits_I - LabelFontUnitsMode - - LabelFontSmoothing - LabelFontSmoothing_I - LabelFontSmoothingMode - - end %properties - -end %classdef \ No newline at end of file diff --git a/widgets/+wt/+mixin/BoundTitleText.m b/widgets/+wt/+mixin/BoundTitleText.m deleted file mode 100644 index 72fef2e4..00000000 --- a/widgets/+wt/+mixin/BoundTitleText.m +++ /dev/null @@ -1,198 +0,0 @@ -classdef BoundTitleText < handle - % Mixin that binds title properties for primitive text object(s) - - %% Protected properties - - properties (Access = protected) - - % The internal title object(s) to bind to - BoundTitle (:,1) matlab.graphics.primitive.Text - - end %properties - - - %% Public Dependent Properties (bound to contents) - properties (AbortSet, Dependent) - - % Axes Title Text - Title (:,1) string - - % Title Font Color - TitleColor - - % Title Font name - TitleFontName - - % Title Font size in points - TitleFontSize - - % Title Font weight (normal/bold) - TitleFontWeight - - % Title Interpreter - TitleInterpreter - - end %properties - - - %% Property Accessors - methods - - function value = get.BoundTitle(obj) - value = obj.BoundTitle; - value(~isvalid(value)) = []; - end - - function value = get.Title(obj) - value = obj.getTitleStrings(); - end - function set.Title(obj, value) - obj.setTitleStrings(value); - end - - function value = get.TitleColor(obj) - value = obj.getFirstTitlePropertyValue("Color"); - end - function set.TitleColor(obj, value) - obj.setAllTitlesToMatchingPropertyValue("Color", value); - end - - function value = get.TitleFontName(obj) - value = obj.getFirstTitlePropertyValue("FontName"); - end - function set.TitleFontName(obj, value) - obj.setAllTitlesToMatchingPropertyValue("FontName", value); - end - - function value = get.TitleFontSize(obj) - value = obj.getFirstTitlePropertyValue("FontSize"); - end - function set.TitleFontSize(obj, value) - obj.setAllTitlesToMatchingPropertyValue("FontSize", value); - end - - function value = get.TitleFontWeight(obj) - value = obj.getFirstTitlePropertyValue("FontWeight"); - end - function set.TitleFontWeight(obj, value) - obj.setAllTitlesToMatchingPropertyValue("FontWeight", value); - end - - function value = get.TitleInterpreter(obj) - value = obj.getFirstTitlePropertyValue("Interpreter"); - end - function set.TitleInterpreter(obj, value) - obj.setAllTitlesToMatchingPropertyValue("Interpreter", value); - end - - end %methods - - - %% Private methods - methods (Access = private) - - function value = getTitleStrings(obj) - - % Get all title objects - allTitle = obj.BoundTitle; - - % Default output - value = strings(size(allTitle)); - - % Get the string of each title - isKeep = isvalid(allTitle); - [value(isKeep)] = string({allTitle.String}'); - - end %function - - - function setTitleStrings(obj, value) - - arguments - obj (1,1) - value (:,1) string - end - - % Get all title objects - allTitle = obj.BoundTitle; - - % How many can we set? - numItems = min(numel(allTitle), numel(value)); - - % Loop on each - isKeep = isvalid(allTitle); - for idx = 1:numItems - if isKeep(idx) - allTitle(idx).String = value(idx); - end - end - - end %function - - function value = getFirstTitlePropertyValue(obj, propName) - - % Store a default object in case no titles exist yet - persistent defaultTitle - if isempty(defaultTitle) - defaultTitle = matlab.graphics.primitive.Text("Parent",[]); - end - - % Get the first valid title - allTitle = obj.BoundTitle(isvalid(obj.BoundTitle)); - if isempty(allTitle) - firstTitle = defaultTitle; - else - firstTitle = allTitle(1); - end - - % Get the property requested - value = firstTitle.(propName); - - end %function - - - function setAllTitlesToMatchingPropertyValue(obj, propName, value) - - % Get valid title objects - allTitle = obj.BoundTitle(isvalid(obj.BoundTitle)); - - % Set the property requested - wt.utility.fastSet(allTitle, propName, value) - - end %function - - end %methods - - - %% Reserved for future use - % These may be added in the future. Defining them here so that - % subclasses should not use these reserved properties yet. - properties (Transient, GetAccess = protected, SetAccess = immutable) - - TitleFontName_I - TitleFontNameMode - - TitleFontSize_I - TitleFontSizeMode - - TitleFontWeight_I - TitleFontWeightMode - - TitleColor_I - TitleColorMode - - TitleFontAngle - TitleFontAngle_I - TitleFontAngleMode - - TitleFontUnits - TitleFontUnits_I - TitleFontUnitsMode - - TitleFontSmoothing - TitleFontSmoothing_I - TitleFontSmoothingMode - - end %properties - -end %classdef \ No newline at end of file diff --git a/widgets/doc/AdvancedAppZooHierarchyExample.html b/widgets/doc/AdvancedAppZooHierarchyExample.html index 0c7c145a..4b336dd9 100644 --- a/widgets/doc/AdvancedAppZooHierarchyExample.html +++ b/widgets/doc/AdvancedAppZooHierarchyExample.html @@ -1,21 +1,21 @@ -Advanced Application Development: Zoo Hierarchy App

Advanced Application Development: Zoo Hierarchy App

This example presents an example of an advanced app developed using Widgets Toolbox. The app combines several advanced concepts that help to optimize efficient development workflows and ensure the app is scalable and testable. The concepts used include:

App Example: Zoo Hierarchy

While MATLAB apps serve a wide variety of needs, a frequent app requirement involves viewing and manipulating a hierarchy data. The Zoo Hierarchy example app (Figure 1) has been designed specifically to provide a simplistic and understandable example for learning how to implement data hierarchy in a modular and programmatically written MATLAB app. This app manages data from a top level, in this case being the entirety of a zoo, down to the lowest level, the individual animals residing there.
ZooAppOverview.png
Figure 1. The zoo hierarchy example app.

Running the Example

Run the example by calling the application's launcher:
app = WidgetsZooAppLauncher;
Note that this launcher will pre-populate the app with a sample dataset. The optional output argument app provides a handle reference to the app. It is an instance of class zooexample.app.ZooHierarchy.
Use the tree control on the left side of the app to navigate through the hierarchy, viewing the details on the right side:
ZooAppExhibit.png

How the Back-end Software Architecture was Implemented

The Back-end Data Hierarchy

This app enables the user to view and edit data within a tree hierarchy. The tree has multiple different levels described below:
  1. Session: A single collection of data, also referred to as a "document", in this case corresponding to an entire zoo. A session may contain one or more Exhibit objects inside.
  2. Exhibit: A subset of a zoo, corresponding to one section or area. An exhibit may have one or more Enclosure objects within it.
  3. Enclosure: A specific cage enclosure, tank, etc. within an Exhibit. Multiple animals may stay within an enclosure.
  4. Animal: An individual animal within an Enclosure. This is the lowest level of the hierarchy.
ZooAppTreeHierarchy.png

Class Objects Representing the Data

Each of the items in the hierarchy was implemented using a MATLAB class object. These class objects were linked as a "parent / child" hierarchy. This is shown in Figure 2 below, a Unified Modeling Language (UML) class diagram:
ZooAppBasicHierarchyUML.png
Figure 2. UML class diagram showing data hierarchy for the zoo session.
Each of the above class objects provides properties to store the relevant metadata for a given entity. The first three objects also provide nested hierarchy where a property contains an array of other objects that belong to it. This type of object hierarchy is known as composition or aggregation from an object-oriented design perspective.

Implementation of the Back-end Model

These class objects were created to represent the model for the app, the entity that comprises the app's data, states, and algorithms. As previously explained and shown in Figure 2, the model is implemented using four different class objects:
The backend model objects were implemented as handle classes, meaning the object that is passed by reference. This enabled the user's session to be in a consistent state. Multiple different view/controller modules within the app could reference these model classes simultaneously if needed and stay synchronized to the current state.
(Please refer to this article for a discussion of handle vs. value classes: Developing MATLAB Apps Using the Model-View-Controller Pattern - MATLAB & Simulink)
For convenience and code modularity reasons, reusable superclasses were developed to provide common functionality of these "model classes". The classes BaseModel and BaseSession provide the concrete classes (Session, Exhibit, Enclosure, Animal) with change notification from a public PropertyChanged event. This provides a simple mechanism for the view/controller classes displaying a model to listen to model changes and be alerted if a display update needs to happen.
The expanded UML hierarchy including superclasses is shown in Figure 3:
ZooAppBackendUML.png
Figure 3. UML class diagram showing superclass hierarchy, with the four concrete model classes at the bottom

How the Front-end Views and Controllers were Implemented to Display the Model Classes

Hybrid View/Controller Components

In the case of this app, the view and controller functions are desired to be shown together, so hybrid view/controller classes were implemented for each of the four views. The app has four different types of views, one for each of the four model classes. Each of these four model classes has a corresponding view/controller component. These are shown in Figure 4:
ZooAppViewsUML.png
Figure 4. UML class diagram showing (four lower boxes) the view/controller components that display their respective model classes

Example

Aside from use in the app itself, these view/controller components are individual standalone and reusable modules.
Here we can create and populate an Animal model class instance:
animalA = zooexample.model.Animal;
animalA.Species = "Lion";
animalA.Name = "Simba";
animalA.Sex = "male";
animalA.BirthDate = "March 9, 2017";
disp(animalA)
Animal with properties: +.S15 { margin: 10px 10px 5px 4px; padding: 0px; line-height: 18px; min-height: 0px; white-space: pre-wrap; color: rgb(33, 33, 33); font-family: Helvetica, Arial, sans-serif, Helvetica, Arial, sans-serif; font-style: normal; font-size: 15px; font-weight: 700; text-align: left; }

Advanced Application Example: Zoo Hierarchy App

This example presents an example of an advanced app developed using Widgets Toolbox. The app combines several advanced concepts that help to optimize efficient development workflows and ensure the app is scalable and testable. The concepts used include:

App Example: Zoo Hierarchy

While MATLAB apps serve a wide variety of needs, a frequent app requirement involves viewing and manipulating a hierarchy data. The Zoo Hierarchy example app (Figure 1) has been designed specifically to provide a simplistic and understandable example for learning how to implement data hierarchy in a modular and programmatically written MATLAB app. This app manages data from a top level, in this case being the entirety of a zoo, down to the lowest level, the individual animals residing there.
ZooAppOverview.png
Figure 1. The zoo hierarchy example app.

Running the Example

Run the example by calling the application's launcher:
app = WidgetsZooAppLauncher;
Note that this launcher will pre-populate the app with a sample dataset. The optional output argument app provides a handle reference to the app. It is an instance of class zooexample.app.ZooHierarchy.
Use the tree control on the left side of the app to navigate through the hierarchy, viewing the details on the right side:
ZooAppExhibit.png

How the Back-end Software Architecture was Implemented

The Back-end Data Hierarchy

This app enables the user to view and edit data within a tree hierarchy. The tree has multiple different levels described below:
  1. Session: A single collection of data, also referred to as a "document", in this case corresponding to an entire zoo. A session may contain one or more Exhibit objects inside.
  2. Exhibit: A subset of a zoo, corresponding to one section or area. An exhibit may have one or more Enclosure objects within it.
  3. Enclosure: A specific cage enclosure, tank, etc. within an Exhibit. Multiple animals may stay within an enclosure.
  4. Animal: An individual animal within an Enclosure. This is the lowest level of the hierarchy.
ZooAppTreeHierarchy.png

Class Objects Representing the Data

Each of the items in the hierarchy was implemented using a MATLAB class object. These class objects were linked as a "parent / child" hierarchy. This is shown in Figure 2 below, a Unified Modeling Language (UML) class diagram:
ZooAppBasicHierarchyUML.png
Figure 2. UML class diagram showing data hierarchy for the zoo session.
Each of the above class objects provides properties to store the relevant metadata for a given entity. The first three objects also provide nested hierarchy where a property contains an array of other objects that belong to it. This type of object hierarchy is known as composition or aggregation from an object-oriented design perspective.

Implementation of the Back-end Model

These class objects were created to represent the model for the app, the entity that comprises the app's data, states, and algorithms. As previously explained and shown in Figure 2, the model is implemented using four different class objects:
The backend model objects were implemented as handle classes, meaning the object that is passed by reference. This enabled the user's session to be in a consistent state. Multiple different view/controller modules within the app could reference these model classes simultaneously if needed and stay synchronized to the current state.
(Please refer to this article for a discussion of handle vs. value classes: Developing MATLAB Apps Using the Model-View-Controller Pattern - MATLAB & Simulink)
For convenience and code modularity reasons, reusable superclasses were developed to provide common functionality of these "model classes". The classes BaseModel and BaseSession provide the concrete classes (Session, Exhibit, Enclosure, Animal) with change notification from a public PropertyChanged event. This provides a simple mechanism for the view/controller classes displaying a model to listen to model changes and be alerted if a display update needs to happen.
The expanded UML hierarchy including superclasses is shown in Figure 3:
ZooAppBackendUML.png
Figure 3. UML class diagram showing superclass hierarchy, with the four concrete model classes at the bottom

How the Front-end Views and Controllers were Implemented to Display the Model Classes

Hybrid View/Controller Components

In the case of this app, the view and controller functions are desired to be shown together, so hybrid view/controller classes were implemented for each of the four views. The app has four different types of views, one for each of the four model classes. Each of these four model classes has a corresponding view/controller component. These are shown in Figure 4:
ZooAppViewsUML.png
Figure 4. UML class diagram showing (four lower boxes) the view/controller components that display their respective model classes

Example

Aside from use in the app itself, these view/controller components are individual standalone and reusable modules.
Here we can create and populate an Animal model class instance:
animalA = zooexample.model.Animal;
animalA.Species = "Lion";
animalA.Name = "Simba";
animalA.Sex = "male";
animalA.BirthDate = "March 9, 2017";
disp(animalA)
Animal with properties: Species: "Lion" BirthDate: 09-Mar-2017 Sex: male Age: 7.8600 - Name: "Simba"
Then, create an Animal view placed within a figure and provide it with the model to display:
fig1 = uifigure("Position",[100 100 250 220]);
grid1 = uigridlayout(fig1,[1 1]);
view1 = zooexample.view.Animal(grid1, "Model", animalA);
This view is synchroniced with the model such that if you modify the model externally, the view will be notified and updates automatically:
animalA.BirthDate = datetime("today");
The birthdate and age are updated to reflect the change.

How the Overall App was Implemented

App Superclasses

The top level of the Zoo Hierarchy App is a hand-coded class file. Its UML class diagram is shown in Figure 5:
ZooAppBasicUML.png
Figure 5. UML class diagram showing the Zoo Hierarchy app and its superclasses
The zooexample.app.ZooHierarchy class implements the functionality unique to the ZooHierarchy main application, but leaves common functionalities to inherited and superclasses that are reusable for other applications. In addition, functionalities that not specific to the Zoo Hierarchy app and are commonly needed for other apps are implemented in superclasses that are inherited by the ZooHierarchy class. Superclass functionalities implemented in BaseApp, AbstractSessionApp, and BaseMultiSessionApp include:
  • Encapsulating and preparing the figure window.
  • Management of user preferences, such as recent folders and initial window size on launch.
  • Management of the user's session state (or "document") being used in the app.
  • Save and load of these user sessions to a file.
  • Listening to session changes to handle "dirty" state, prompting to save changes before closing as necessary.
For further information about these app superclasses, please see: Advanced Application Development: Reusable Superclasses for Hand-Code Apps

ZooHierarchy Main App Implementation

The zooexample.app.ZooHierarchy class itself implements the upper toolbar, the left tree hierarchy, and the placement of contextual contents into the right pane. The actual content that gets placed in the right pane is implemented separately in the view/controller classes previously discussed.
At a high level, this class definition includes:
  • Properties to store internal components.
  • Creation and update of the graphical components.
  • Callback functions for user controls implmented at this level.
  • Helper functions to help manage the above items.

Properties to Store Internal Components

Internal components are stored in properties upon the app's setup task during creation. This enables them to be accessible during callbacks, updates, and other internal functions. Components within this top-level app include:
  • The toolbar at the top, containing buttons for save/load session and other functions.
  • The left-side tree for navigation.
  • The ContextualView pane on the right for displaying a view of the selected tree node's data.

Creation and Update of Graphical Components

The application class implements methods that manage the graphical state:
  • The setup method, responsible for creating the set of components and controls when the app is launched.
  • The update method, responsible for updating main app level content when a model state change has been detected.
  • updateTreeHierarchy is called by the update method when the tree hierarchy needs to be redrawn. This is implemented in a separete .m file within the @ZooHierarchy folder because it is a long and complex method. It parses the hierarchy of the app's Session property recursively and synchronizes the hierarchy of tree nodes to match the contents.

Callback Functions for User Controls

Several callbacks are implemented as methods of the ZooHierarchy class:
  • onFileButtonPushed and onExhibitButtonPushed handle user interaction with the upper toolbar.
  • onTreeSelection handles user interaction with the tree, triggering the appropriate right-side contextual pane to be launched. It also provides the relevant model reference for the given tree node.

Context pane switching

The right side of the app contains a wt.ContextualView widget. This widget provides a container for switching between multiple view/controller components. You can think of it as being similar to a tabpanel, however the contents are switched programmatically. For more information about this widget, please see Contextual View Widget.
Copyright 2025 The MathWorks, Inc.
+ Name: "Simba"
Then, create an Animal view placed within a figure and provide it with the model to display:
fig1 = uifigure("Position",[100 100 250 220]);
grid1 = uigridlayout(fig1,[1 1]);
view1 = zooexample.view.Animal(grid1, "Model", animalA);
This view is synchroniced with the model such that if you modify the model externally, the view will be notified and updates automatically:
animalA.BirthDate = datetime("today");
The birthdate and age are updated to reflect the change.

How the Overall App was Implemented

App Superclasses

The top level of the Zoo Hierarchy App is a hand-coded class file. Its UML class diagram is shown in Figure 5:
ZooAppBasicUML.png
Figure 5. UML class diagram showing the Zoo Hierarchy app and its superclasses
The zooexample.app.ZooHierarchy class implements the functionality unique to the ZooHierarchy main application, but leaves common functionalities to inherited and superclasses that are reusable for other applications. In addition, functionalities that not specific to the Zoo Hierarchy app and are commonly needed for other apps are implemented in superclasses that are inherited by the ZooHierarchy class. Superclass functionalities implemented in BaseApp, AbstractSessionApp, and BaseMultiSessionApp include:
  • Encapsulating and preparing the figure window.
  • Management of user preferences, such as recent folders and initial window size on launch.
  • Management of the user's session state (or "document") being used in the app.
  • Save and load of these user sessions to a file.
  • Listening to session changes to handle "dirty" state, prompting to save changes before closing as necessary.
For further information about these app superclasses, please see: Advanced Application Development: Reusable Superclasses for Hand-Code Apps

ZooHierarchy Main App Implementation

The zooexample.app.ZooHierarchy class itself implements the upper toolbar, the left tree hierarchy, and the placement of contextual contents into the right pane. The actual content that gets placed in the right pane is implemented separately in the view/controller classes previously discussed.
At a high level, this class definition includes:
  • Properties to store internal components.
  • Creation and update of the graphical components.
  • Callback functions for user controls implmented at this level.
  • Helper functions to help manage the above items.

Properties to Store Internal Components

Internal components are stored in properties upon the app's setup task during creation. This enables them to be accessible during callbacks, updates, and other internal functions. Components within this top-level app include:
  • The toolbar at the top, containing buttons for save/load session and other functions.
  • The left-side tree for navigation.
  • The ContextualView pane on the right for displaying a view of the selected tree node's data.

Creation and Update of Graphical Components

The application class implements methods that manage the graphical state:
  • The setup method, responsible for creating the set of components and controls when the app is launched.
  • The update method, responsible for updating main app level content when a model state change has been detected.
  • updateTreeHierarchy is called by the update method when the tree hierarchy needs to be redrawn. This is implemented in a separete .m file within the @ZooHierarchy folder because it is a long and complex method. It parses the hierarchy of the app's Session property recursively and synchronizes the hierarchy of tree nodes to match the contents.

Callback Functions for User Controls

Several callbacks are implemented as methods of the ZooHierarchy class:
  • onFileButtonPushed and onExhibitButtonPushed handle user interaction with the upper toolbar.
  • onTreeSelection handles user interaction with the tree, triggering the appropriate right-side contextual pane to be launched. It also provides the relevant model reference for the given tree node.

Context pane switching

The right side of the app contains a wt.ContextualView widget. This widget provides a container for switching between multiple view/controller components. You can think of it as being similar to a tabpanel, however the contents are switched programmatically. For more information about this widget, please see Contextual View Widget.
Copyright 2025 The MathWorks, Inc.