-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathTabManager.m
More file actions
184 lines (169 loc) · 8.5 KB
/
Copy pathTabManager.m
File metadata and controls
184 lines (169 loc) · 8.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
classdef TabManager < handle
% TABMANAGER - Converts panels into Tab Panes based on tag name convention
%
% Panels names Tab? (e.g. TabA) are used to position a tabgroup that is then
% populated with panes from Tab?* (e.g. TabAChild1)
%
% Useful with Matlab GUIDE created windows
%
% GUIDE Usage
% 1. Create a pane with tag set to Tab? where ? is any letter or number
% (e.g. TabA). This main pane should be left empty and determines the size
% and location of the tab group (uitabgroup)
% 2. Create additional panes with a tag name that starts with the name of
% the main pane. All other controls should be added to these panes.
% 3. In the Guide generated function xxx_OpeningFcn add the following:
% handles.tabManager = TabManager( hObject );
%
% Notes:
% 1. The panes can all be moved to the same location as long as they are
% not placed inside each other. You can edit the hidden panes by
% using the "Send to back" option on the GUIDE pop-up menu.
% 2. The pane tag names are sorted alphabetically before the panels
% are created. One way of making this more obvious is by inserting
% ordering digit(s) in the panel tag names.
% For example, the panels with the following details:
%
% Title Tag
% ----------------------------------
% Main TabA02Main
% Supplementary TabA01Supplementary
%
% will create the Supplementary tab first with the Main tab second.
% 3. Tabs can be selected programmatically by setting the SelectedTab
% property of the tab group to the required tab.
% For example:
% tabMan = handles.tabManager;
% tabMan.Handles.TabA.SelectedTab = tabMan.Handles.TabA02Supplementary;
% 4. You can set a selection changed callback on tab groups so that
% your program is notified when a user clicks on a tab.
%
% TabManagerExample.m shows how to do this.
%
% Date Version Author Notes
% 16/08/2015 1.00 Grant Davidson Initial release
% 13/03/2016 1.01 Grant Davidson Added note re Tab ordering
% 4/07/2016 1.02 Grant Davidson Added note re Tab selection
% 9/6/2017 1.03 Grant Davidson Check that panes representing
% tabs aren't children of the
% main pane.
% 17/6/2017 1.04 Grant Davidson Added note re selection change
% callback
% 13/7/2017 1.05 Grant Davidson Panel BackgroundColor property
% now copied to uitab
% 11/3/2018 1.06 Grant Davidson Tab Order of children now maintained.
% Nested tabs supported (Thanks Timon)
properties
GuiFigure
Handles
end
properties(Constant,Access='private')
TabPrefix = 'Tab';
end
properties(Dependent)
TabGroups
end
properties(Hidden)
TabGroupsInternal
end
methods(Access=private)
function value = GetAllTabPanelsFieldNames( self )
fields = fieldnames( self.Handles );
fieldIndices = strncmp(fields,TabManager.TabPrefix, length(TabManager.TabPrefix));
value = fields(fieldIndices);
end
function tabOwnerPanels = GetTabOwnerPanels( self )
allTabPanels = self.GetAllTabPanelsFieldNames( );
owners = find(cellfun( @(panelFieldName) length(panelFieldName) == length(TabManager.TabPrefix) + 1, allTabPanels));
ownerFields = allTabPanels(owners);
tabOwnerPanels = zeros(size(owners));
for fi=1:length(ownerFields)
tabOwnerPanels(fi) = self.Handles.(ownerFields{fi});
end
end
end
methods
function self = TabManager( guiFigure )
self.GuiFigure = guiFigure;
self.Handles = guihandles( self.GuiFigure );
ownerPanels = self.GetTabOwnerPanels( );
self.TabGroupsInternal = zeros(size(ownerPanels));
for oi=1:length(ownerPanels)
panelOwner = ownerPanels(oi);
[panelsForOwner, panelNamesForOwner] = self.GetTabPanelsForOwner( panelOwner );
TabManager.VerifyPanelParentage( panelsForOwner, panelOwner );
pos = get(panelOwner,'Position');
units = get(panelOwner,'Units');
ownerTag = get(panelOwner,'Tag');
parent = get(panelOwner,'Parent');
delete(panelOwner);
tabGroup = uitabgroup('Parent', parent, 'Units', units, 'Position', pos, 'Tag', ownerTag );
for pi=1:length(panelsForOwner)
hPanel = panelsForOwner(pi);
if ishandle(hPanel)
panelTag = get(hPanel,'Tag');
panelTitle = get(hPanel,'Title');
panelBackground = get(hPanel,'BackgroundColor');
if iscell(panelTitle)
panelTitle = cell2mat(panelTitle);
end
tab = uitab('Parent', tabGroup, 'Title', panelTitle);
children = get(hPanel,'Children');
% We need to add the children in reverse order to
% the new parent because Matlab adds new children
% at position 1 in the new parents Children array.
% It does this because the tab order of child
% components is the reverse of the Children order.
for ci=length(children):-1:1
set(children(ci),'Parent', tab);
end
delete(hPanel);
set(tab,'Tag', panelTag );
set(tab,'BackgroundColor', panelBackground );
%set(hPanel,'Visible', 'off');
else
error('The panel %s (%d of %d) for %s has been unexpectedly deleted.', panelNamesForOwner{pi}, pi, length(panelsForOwner), ownerTag);
end
end
self.TabGroupsInternal(oi) = tabGroup;
end
% Refresh handles (although these currently aren't used outside
% the constructor)
self.Handles = guihandles( self.GuiFigure );
end
function value = get.TabGroups( self )
value = self.TabGroupsInternal;
end
end
methods(Static)
function VerifyPanelParentage( panels, panelOwner )
forbiddenParents = panels;
forbiddenParents(end+1) = panelOwner;
for pi=1:length(panels)
panel = panels(pi);
invalidParent = forbiddenParents(forbiddenParents == get(panel,'Parent'));
if ~isempty(invalidParent)
if panelOwner == invalidParent
error('Panel %s must not be a child of its main pane %s (which must be an empty panel).', ...
get(panel,'Tag'), get(panelOwner,'Tag') );
else
error('Panel %s must not be a child of another panel (%s) in the same tab group.', ...
get(panel,'Tag'), get(invalidParent,'Tag') );
end
end
end
end
end
methods(Access=private)
function [handles, ownerPanelFieldNames] = GetTabPanelsForOwner( self, ownerHandle )
ownerPrefix = get( ownerHandle, 'Tag' );
allTabPanels = self.GetAllTabPanelsFieldNames( );
fieldIndices = strncmp(allTabPanels,ownerPrefix, length(ownerPrefix)) & ~strcmp(allTabPanels,ownerPrefix);
ownerPanelFieldNames = sort(allTabPanels(fieldIndices));
handles = zeros(size(ownerPanelFieldNames));
for fi=1:length(ownerPanelFieldNames)
handles(fi) = self.Handles.(ownerPanelFieldNames{fi});
end
end
end
end