source: MML/trunk/machine/SOLEIL/common/toolbox/GUILayout/+uiextras/TabPanel.m @ 4

Last change on this file since 4 was 4, checked in by zhangj, 10 years ago

Initial import--MML version from SOLEIL@2013

File size: 19.9 KB
Line 
1classdef TabPanel < uiextras.CardPanel & uiextras.DecoratedPanel
2    %TabPanel  Show one element inside a tabbed panel
3    %
4    %   obj = uiextras.TabPanel() creates a panel with tabs along one edge
5    %   to allow selection between the different child objects contained.
6    %
7    %   obj = uiextras.TabPanel(param,value,...) also sets one or more
8    %   property values.
9    %
10    %   See the <a href="matlab:doc uiextras.TabPanel">documentation</a> for more detail and the list of properties.
11    %
12    %   Examples:
13    %   >> f = figure();
14    %   >> p = uiextras.TabPanel( 'Parent', f, 'Padding', 5 );
15    %   >> uicontrol( 'Style', 'frame', 'Parent', p, 'Background', 'r' );
16    %   >> uicontrol( 'Style', 'frame', 'Parent', p, 'Background', 'b' );
17    %   >> uicontrol( 'Style', 'frame', 'Parent', p, 'Background', 'g' );
18    %   >> p.TabNames = {'Red', 'Blue', 'Green'};
19    %   >> p.SelectedChild = 2;
20    %
21    %   See also: uiextras.Panel
22    %             uiextras.BoxPanel
23   
24    %   Copyright 2009-2010 The MathWorks, Inc.
25    %   1.1
26    %   2012/05/08 08:02:58
27   
28    properties
29        TabSize = 50
30        TabPosition = 'top' % which side of the contents to put the tabs [top|bottom]
31    end % public properties
32   
33    properties( Dependent = true )
34        TabNames          % The title string for each tab
35        TabEnable = {}    % The enable state of individual tabs
36    end % dependent properties
37   
38    properties( SetAccess = private, GetAccess = private, Hidden = true )
39        Images_ = struct()
40        TabImage_ = []
41        PageLabels = []
42        PageEnable_ = {}
43    end % private properties
44   
45   
46    methods
47       
48        function obj = TabPanel(varargin)
49            % First step is to create the parent class. We pass the
50            % arguments (if any) just incase the parent needs setting
51            obj = obj@uiextras.CardPanel( varargin{:} );
52            obj = obj@uiextras.DecoratedPanel( varargin{:} );
53           
54            % Get some defaults
55            bgcol = obj.BackgroundColor;
56            obj.HighlightColor = ( 2*[1 1 1] + bgcol )/3;
57            obj.ShadowColor    = 0.5*bgcol;
58
59            % Add a UIControl for drawing the tabs
60            obj.TabImage_ = uicontrol( ...
61                'Visible', 'on', ...
62                'units', 'pixels', ...
63                'Parent', obj.UIContainer, ...
64                'HandleVisibility', 'off', ...
65                'Position', [1 1 1 1], ...
66                'style', 'checkbox', ...
67                'Tag', 'uiextras.TabPanel:TabImage');
68           
69            % Make sure the images are loaded
70            obj.reloadImages();
71           
72            % Set some defaults
73            obj.setPropertyFromDefault( 'HighlightColor' );
74            obj.setPropertyFromDefault( 'ShadowColor' );           
75            obj.setPropertyFromDefault( 'TabPosition' );
76            obj.setPropertyFromDefault( 'TabSize' );
77
78            % Parse any input arguments
79            if nargin>0
80                set( obj, varargin{:} );
81            end
82            obj.redraw();
83        end % TabPanel
84       
85    end % public methods
86   
87    methods
88       
89        function set.TabSize(obj,value)
90            obj.TabSize = value;
91            obj.redraw();
92        end % set.TabSize
93       
94        function set.TabPosition(obj,value)
95            if ~ischar( value ) || ~ismember( lower( value ), {'top','bottom'} )
96                error( 'GUILayout:InvalidPropertyValue', ...
97                    'Property ''TabPosition'' must be ''top'' or ''bottom''.' );
98            end
99            obj.TabPosition = [upper( value(1) ),lower( value(2:end) )];
100            obj.redraw();
101        end % set.TabPosition
102       
103        function value = get.TabNames( obj )
104            if isempty( obj.PageLabels )
105                value = {};
106            elseif numel( obj.PageLabels ) == 1
107                value = {get( obj.PageLabels, 'String' )};
108            else
109                value = get( obj.PageLabels, 'String' )';
110            end
111        end % get.TabNames
112       
113        function set.TabNames(obj,value)
114            if ~iscell( value ) || numel( value )~=numel( obj.Children )
115                error( 'GUILayout:InvalidPropertyValue', ...
116                    'Property ''TabNames'' must be a cell array of strings the same size as property ''Children''.' )
117            end
118            for ii=1:numel( obj.Children )
119                set( obj.PageLabels(ii), 'String', value{ii} );
120            end
121        end % set.TabNames
122
123        function value = get.TabEnable(obj)
124            value = obj.PageEnable_;
125        end % get.TabEnable
126
127        function set.TabEnable(obj,value)
128            if ~iscell( value ) || numel( value )~=numel( obj.Children ) ...
129                    || any( ~ismember( lower(value), {'on','off'} ) )
130                error( 'GUILayout:InvalidPropertyValue', ...
131                    'Property ''TabEnable'' must be a cell array of ''on''/''off'' the same size as property ''Children''.' )
132            end
133            obj.PageEnable_ = lower( value );
134            if strcmpi( obj.Enable, 'on' )
135                obj.onEnable();
136            end
137        end % set.TabEnable
138       
139    end % accessor methods
140   
141    methods ( Access = protected )
142       
143        function redraw(obj)
144            %redraw  Redraw the tabs and contents
145           
146            % Check the object exists (may be being deleted!)
147            if isempty(obj.TabImage_) || ~ishandle(obj.TabImage_)
148                return;
149            end
150           
151            C = obj.Children;
152            T = obj.TabNames;
153           
154            % Make sure label array is right size
155            nC = numel(C);
156            nT = numel(T);
157           
158            if nC==0 || nT~=nC
159                return
160            end
161            pos = getpixelposition( obj.UIContainer );
162           
163            pad = obj.Padding;
164           
165            % Calculate the required height from the font size
166            oldFontUnits = get( obj.PageLabels(1), 'FontUnits' );
167            set( obj.PageLabels(1), 'FontUnits', 'Pixels' );
168            fontHeightPix = get( obj.PageLabels(1), 'FontSize' );
169            set( obj.PageLabels(1), 'FontUnits', oldFontUnits );
170            tabHeight = ceil( 1.5*fontHeightPix + 4 );
171           
172            % Work out where the tabs labels and contents go
173            if strcmpi( obj.TabPosition, 'Top' )
174                tabPos = [1 1+pos(4)-tabHeight, pos(3), tabHeight+2];
175                contentPos = [pad+1 pad+1 pos(3)-2*pad pos(4)-2*pad-tabHeight];
176            else
177                tabPos = [1 1, pos(3), tabHeight+2];
178                contentPos = [pad+1 tabHeight+pad+1 pos(3)-2*pad pos(4)-2*pad-tabHeight];
179            end
180           
181            % Shorthand for colouring things in
182            fgCol = obj.BackgroundColor;
183            bgCol = obj.BackgroundColor;
184            shCol = 0.9*obj.BackgroundColor;
185           
186            totalWidth = round( tabPos(3)-1 );
187            divWidth = 8;
188            textWidth = obj.TabSize;
189            if textWidth<0
190                % This means we should fill the space
191                textWidth = floor( (totalWidth - (nC+1)*divWidth) / nC );
192            end
193            textPos = [tabPos(1:2), textWidth, tabHeight - 4];
194           
195            if ~isempty( obj.SelectedChild )
196                % The tabs are drawn as a single image
197                tabCData(:,:,1) = bgCol(1)*ones(20,totalWidth);
198                tabCData(:,:,2) = bgCol(2)*ones(20,totalWidth);
199                tabCData(:,:,3) = bgCol(3)*ones(20,totalWidth);
200                set( obj.TabImage_, 'Position', [tabPos(1:2),totalWidth,tabHeight] );
201               
202                % Use the CardLayout function to put the right child onscreen
203                obj.showSelectedChild( contentPos )
204               
205                % Now update the tab image
206                tabCData(:,1:divWidth,:) = obj.Images_.NonNot;
207                for ii=1:nC
208                    x = divWidth+(divWidth+textWidth)*(ii-1)+1;
209                    set( obj.PageLabels(ii), ...
210                        'Position', textPos+[x,0,0,0] );
211                    % BJT: Fix bug where text renders off edge of container
212                    if (textPos(1)+x >= totalWidth )
213                        set( obj.PageLabels(ii), 'Visible', 'off' );
214                    else
215                        set( obj.PageLabels(ii), 'Visible', 'on' );
216                        rhs = textPos(1)+x+textPos(3);
217                        if ( rhs > totalWidth )
218                            % Text is partially off the edge
219                            set( obj.PageLabels(ii), 'Position', textPos+[x,0,totalWidth-rhs,0] );
220                        end
221                    end
222                   
223                    if ii==obj.SelectedChild,
224                        set( obj.PageLabels(ii), ...
225                            'ForegroundColor', obj.ForegroundColor, ...
226                            'BackgroundColor', fgCol);
227                        % Set the dividers to show the right image
228                        tabCData(:,x:x+textWidth-1,:) = repmat(obj.Images_.SelBack,1,textWidth);
229                        if ii==1
230                            tabCData(:,x-divWidth:x-1,:) = obj.Images_.NonSel;
231                        else
232                            tabCData(:,x-divWidth:x-1,:) = obj.Images_.NotSel;
233                        end
234                        if ii==nC
235                            tabCData(:,x+textWidth:x+textWidth+divWidth-1,:) = obj.Images_.SelNon;
236                        else
237                            tabCData(:,x+textWidth:x+textWidth+divWidth-1,:) = obj.Images_.SelNot;
238                        end
239                    else
240                        set( obj.PageLabels(ii), ...
241                            'ForegroundColor', 0.6*obj.ForegroundColor + 0.4*shCol, ...
242                            'BackgroundColor', shCol );
243                        tabCData(:,x:x+textWidth-1,:) = repmat(obj.Images_.NotBack,1,textWidth);
244                        if ii==nC
245                            tabCData(:,x+textWidth:x+textWidth+divWidth-1,:) = obj.Images_.NotNon;
246                        else
247                            tabCData(:,x+textWidth:x+textWidth+divWidth-1,:) = obj.Images_.NotNot;
248                        end
249                    end
250                end % For
251               
252                % Stretch the CData to match the fontsize
253                if tabHeight ~= 20
254                    topbot = min( round( tabHeight/2 ), 5 );
255                    midsz = tabHeight - 2*topbot;
256                    topData = tabCData(1:topbot,:,:);
257                    bottomData = tabCData(end-topbot+1:end,:,:);
258                    midData = repmat( tabCData(10,:,:), [midsz,1,1] );
259                    tabCData = [ topData ; midData ; bottomData ];
260                end
261               
262                if strcmpi( obj.TabPosition, 'Top' )
263                    set( obj.TabImage_, 'CData', tabCData );
264                else
265                    set( obj.TabImage_, 'CData', flipdim( tabCData, 1 ) );
266                end
267            end
268           
269           
270            % Make sure the text labels are top of the stack
271            %             ch = get( obj.TabContainer_, 'Children' );
272            %             if numel( ch ) > 1
273            %                 labs = ismember( get(ch,'Style'), 'text' );
274            %             else
275            %                 labs = strcmpi( get(ch,'Style'), 'text' );
276            %             end
277            %             set( obj.TabContainer_, 'Children', [flipud(ch(labs));ch(~labs)] ); % Note the flip is needed so that the text always redraws
278        end % redraw
279       
280        function onChildAdded( obj, source, eventData ) %#ok<INUSD>
281            %onChildAdded: Callback that fires when a child is added to a container.
282            % Select the new addition
283            C = obj.Children;
284            N = numel( C );
285            visible = obj.Visible;
286            title = sprintf( 'Page %d', N );
287           
288            obj.PageLabels(end+1,1) = uicontrol( ...
289                'Visible', visible, ...
290                'style', 'text', ...
291                'enable', 'inactive', ...
292                'string', title, ...
293                'FontName', obj.FontName, ...
294                'FontUnits', obj.FontUnits, ...
295                'FontSize', obj.FontSize, ...
296                'FontAngle', obj.FontAngle, ...
297                'FontWeight', obj.FontWeight, ...
298                'ForegroundColor', obj.ForegroundColor, ...
299                'parent', obj.UIContainer, ...
300                'HandleVisibility', 'off', ...
301                'ButtonDownFcn', {@iTabClicked, obj, N});
302            obj.PageEnable_{1,end+1} = 'on';
303            if strcmpi( obj.Enable, 'off' )
304                set( obj.PageLabels(end), 'Enable', 'off' );
305            end
306            obj.SelectedChild = N;
307        end % onChildAdded
308       
309        function onChildRemoved( obj, source, eventData ) %#ok<INUSL>
310            %onChildAdded: Callback that fires when a container child is destroyed or reparented.
311            % If the missing child is the selected one, select something else
312            obj.TabNames( eventData.ChildIndex ) = [];
313            obj.PageEnable_( eventData.ChildIndex ) = [];
314            delete( obj.PageLabels(end) );
315            obj.PageLabels(end) = [];
316            if obj.SelectedChild >= eventData.ChildIndex
317                % Changing the selection will force a redraw
318                if isempty( obj.Children )
319                    obj.SelectedChild = [];
320                else
321                    obj.SelectedChild = max( 1, obj.SelectedChild - 1 );
322                end
323            else
324                % We don't need to change the selection, so explicitly
325                % redraw
326                obj.redraw();
327            end
328        end % onChildRemoved
329       
330        function onBackgroundColorChanged( obj, source, eventData ) %#ok<INUSD>
331            %onBackgroundColorChanged  Callback that fires when the container background color is changed
332            %
333            % We need to make the panel match the container background
334            obj.reloadImages();
335            obj.redraw();
336        end % onBackgroundColorChanged
337       
338        function onPanelColorChanged( obj, source, eventData ) %#ok<INUSD>
339            % Colors have changed. This requires the images to be reset and
340            % redrawn.
341            obj.reloadImages();
342            obj.redraw();
343        end % onPanelColorChanged
344       
345        function onPanelFontChanged( obj, source, eventData ) %#ok<INUSL>
346            % Font has changed. Since the font size and shape affects the
347            % space available for the contents, we need to redraw.
348            for ii=1:numel( obj.PageLabels )
349                set( obj.PageLabels(ii), eventData.Property, eventData.Value );
350            end
351            obj.redraw();
352        end % onPanelFontChanged
353       
354        function onEnable( obj, source, eventData ) %#ok<INUSD>
355            % We use "inactive" to be the "on" state
356            if strcmpi( obj.Enable, 'on' )
357                % Take notice of the individual enable states. Where the
358                % page is to be enabled we set the title uicontrol to be
359                % inactive rather than on to avoid mouse-over problems.
360                hittest = obj.PageEnable_;
361                enable = strrep( obj.PageEnable_, 'on', 'inactive' );
362                for jj=1:numel( obj.PageLabels )
363                    set( obj.PageLabels(jj), ...
364                        'Enable', enable{jj}, ...
365                        'HitTest', hittest{jj} );
366                    % Since the panel as a whole is on, we may need to
367                    % switch off some children
368                    obj.helpSetChildEnable( obj.Children(jj), hittest{jj} );
369                end
370            else
371                for jj=1:numel( obj.PageLabels )
372                    set( obj.PageLabels(jj), 'Enable', 'off', 'HitTest', 'off' );
373                end
374            end
375        end % onEnable
376       
377        function reloadImages( obj )
378            % Reload tab images
379           
380            % If any of the colours are not yet constructed, stop now
381            if isempty( obj.TabImage_ ) ...
382                    || isempty( obj.HighlightColor ) ...
383                    || isempty( obj.ShadowColor )
384                return;
385            end
386           
387            % First part of the name says which type of right-hand edge is needed
388            % (non = no edge, not = not selected, sel = selected), second gives
389            % left-hand
390            obj.Images_.NonSel = iLoadIcon( 'tab_NoEdge_Selected.png', ...
391                obj.BackgroundColor, obj.HighlightColor, obj.ShadowColor );
392            obj.Images_.SelNon = iLoadIcon( 'tab_Selected_NoEdge.png', ...
393                obj.BackgroundColor, obj.HighlightColor, obj.ShadowColor );
394            obj.Images_.NonNot = iLoadIcon( 'tab_NoEdge_NotSelected.png', ...
395                obj.BackgroundColor, obj.HighlightColor, obj.ShadowColor );
396            obj.Images_.NotNon = iLoadIcon( 'tab_NotSelected_NoEdge.png', ...
397                obj.BackgroundColor, obj.HighlightColor, obj.ShadowColor );
398            obj.Images_.NotSel = iLoadIcon( 'tab_NotSelected_Selected.png', ...
399                obj.BackgroundColor, obj.HighlightColor, obj.ShadowColor );
400            obj.Images_.SelNot = iLoadIcon( 'tab_Selected_NotSelected.png', ...
401                obj.BackgroundColor, obj.HighlightColor, obj.ShadowColor );
402            obj.Images_.NotNot = iLoadIcon( 'tab_NotSelected_NotSelected.png', ...
403                obj.BackgroundColor, obj.HighlightColor, obj.ShadowColor );
404            obj.Images_.SelBack = iLoadIcon( 'tab_Background_Selected.png', ...
405                obj.BackgroundColor, obj.HighlightColor, obj.ShadowColor );
406            obj.Images_.NotBack = iLoadIcon( 'tab_Background_NotSelected.png', ...
407                obj.BackgroundColor, obj.HighlightColor, obj.ShadowColor );
408           
409        end % reloadImages
410       
411    end % protected methods
412   
413end % classdef
414
415%-------------------------------------------------------------------------%
416
417function im = iLoadIcon(imagefilename, backgroundcolor, highlightcolor, shadowcolor )
418% Special image loader that turns various primary colours into background
419% colours.
420
421error( nargchk( 4, 4, nargin, 'struct' ) );
422
423% Load an icon and set the transparent color
424this_dir = fileparts( mfilename( 'fullpath' ) );
425icon_dir = fullfile( this_dir, 'Resources' );
426im8 = imread( fullfile( icon_dir, imagefilename ) );
427im = double(im8)/255;
428rows = size(im,1);
429cols = size(im,2);
430
431% Anything that's pure green goes to transparent
432f=find((im8(:,:,1)==0) & (im8(:,:,2)==255) & (im8(:,:,3)==0));
433im(f) = nan;
434im(f + rows*cols) = nan;
435im(f + 2*rows*cols) = nan;
436
437% Anything pure red goes to selected background
438f=find((im8(:,:,1)==255) & (im8(:,:,2)==0) & (im8(:,:,3)==0));
439im(f) = backgroundcolor(1);
440im(f + rows*cols) = backgroundcolor(2);
441im(f + 2*rows*cols) = backgroundcolor(3);
442
443% Anything pure blue goes to background background
444f=find((im8(:,:,1)==0) & (im8(:,:,2)==0) & (im8(:,:,3)==255));
445im(f) = backgroundcolor(1);
446im(f + rows*cols) = backgroundcolor(2);
447im(f + 2*rows*cols) = backgroundcolor(3);
448
449% Anything pure yellow goes to deselected background
450f=find((im8(:,:,1)==255) & (im8(:,:,2)==255) & (im8(:,:,3)==0));
451im(f) = 0.9*backgroundcolor(1);
452im(f + rows*cols) = 0.9*backgroundcolor(2);
453im(f + 2*rows*cols) = 0.9*backgroundcolor(3);
454
455% Anything pure white goes to highlight
456f=find((im8(:,:,1)==255) & (im8(:,:,2)==255) & (im8(:,:,3)==255));
457im(f) = highlightcolor(1);
458im(f + rows*cols) = highlightcolor(2);
459im(f + 2*rows*cols) = highlightcolor(3);
460
461% Anything pure black goes to shadow
462f=find((im8(:,:,1)==0) & (im8(:,:,2)==0) & (im8(:,:,3)==0));
463im(f) = shadowcolor(1);
464im(f + rows*cols) = shadowcolor(2);
465im(f + 2*rows*cols) = shadowcolor(3);
466
467end % iLoadIcon
468
469%-------------------------------------------------------------------------%
470
471function iTabClicked( src, evt, obj, idx ) %#ok<INUSL>
472
473% Call the user callback before selecting the tab
474evt = struct( ...
475    'Source', obj, ...
476    'PreviousChild', obj.SelectedChild, ...
477    'SelectedChild', idx );
478uiextras.callCallback( obj.Callback, obj, evt );
479obj.SelectedChild = idx;
480
481end % iTabClicked
Note: See TracBrowser for help on using the repository browser.