classdef Panel < uiextras.CardPanel & uiextras.DecoratedPanel
    %Panel  Show one element inside a panel
    %
    %   obj = uiextras.Panel() creates a standard UIPANEL object but with
    %   automatic management of the contained widget or layout. The
    %   properties available are largely the same as the builtin UIPANEL
    %   object. Where more than one child is added, the currently visible
    %   child is determined using the SelectedChild property.
    %
    %   obj = uiextras.Panel(param,value,...) also sets one or more
    %   property values.
    %
    %   See the <a href="matlab:doc uiextras.Panel">documentation</a> for more detail and the list of properties.
    %
    %   Examples:
    %   >> f = figure();
    %   >> p = uiextras.Panel( 'Parent', f, 'Title', 'A Panel', 'Padding', 5 );
    %   >> uicontrol( 'Parent', p, 'Background', 'r' )
    %
    %   >> f = figure();
    %   >> p = uiextras.Panel( 'Parent', f, 'Title', 'A Panel', 'Padding', 5 );
    %   >> b = uiextras.HBox( 'Parent', p, 'Spacing', 5 );
    %   >> uicontrol( 'Style', 'listbox', 'Parent', b, 'String', {'Item 1','Item 2'} );
    %   >> uicontrol( 'Parent', b, 'Background', 'b' );
    %   >> set( b, 'Sizes', [100 -1] );
    %
    %   See also: uipanel
    %             uiextras.BoxPanel
    %             uiextras.HBox
    
    %   Copyright 2009-2010 The MathWorks, Inc.
    %   $Revision: 357 $
    %   $Date: 2010-11-08 08:19:10 +0000 (Mon, 08 Nov 2010) $
    
    properties( Dependent = true )
        BorderType      % Type of border around the uipanel area  [none|etchedin|etchedout|beveledin|beveledout|line]
        BorderWidth     % Width of the panel border
        Title           % Title string
        TitlePosition   % Location of title string in relation to the panel [lefttop|centertop|righttop|leftbottom|centerbottom|rightbottom]
    end % dependent properties
    
    methods
        
        function obj = Panel( varargin )
            %PANEL constructor
            
            % First step is to create the parent class. We pass the
            % arguments (if any) just incase the parent needs setting
            obj = obj@uiextras.CardPanel( varargin{:} );
            obj = obj@uiextras.DecoratedPanel( varargin{:} );
            
            set( obj.UIContainer, 'Title', '' );
            
            % Override base settings
            obj.BorderType = 'etchedin';
            obj.BorderWidth = 1;
            obj.TitlePosition = 'lefttop';
            
            % See if the user has set any defaults
            obj.setPropertyFromDefault( 'BorderType' );
            obj.setPropertyFromDefault( 'BorderWidth' );
            obj.setPropertyFromDefault( 'TitlePosition' );
            
            % Set user-supplied property values
            if nargin > 0
                set( obj, varargin{:} );
            end
        end % Panel
        
    end % public methods
    
    methods
        
        function value = get.BorderType( obj )
            value = get( obj.UIContainer, 'BorderType' );
        end % get.BorderType
        
        function set.BorderType( obj, value )
            set( obj.UIContainer, 'BorderType', value );
        end % set.BorderType
        
        function value = get.BorderWidth( obj )
            value = get( obj.UIContainer, 'BorderWidth' );
        end % get.BorderWidth
        
        function set.BorderWidth( obj, value )
            set( obj.UIContainer, 'BorderWidth', value );
            obj.redraw();
        end % set.BorderWidth
        
        function value = get.Title( obj )
            value = get( obj.UIContainer, 'Title' );
        end % get.Title
        
        function set.Title( obj, value )
            % Only need to redraw if changing to/from empty
            oldValue = get( obj.UIContainer, 'Title' );
            % Unfortunately setting the title creates a uicontrol that
            % isn't tagged, so we have to set a flag so that we know to
            % ignore it for resize purposes
            setappdata( obj.UIContainer, 'PanelTitleCreate', true );
            set( obj.UIContainer, 'Title', value );
            rmappdata( obj.UIContainer, 'PanelTitleCreate' );
            if isempty( value ) && ~isempty( oldValue )
                obj.redraw();
            elseif isempty( oldValue ) && ~isempty( value )
                obj.redraw();
            end
        end % set.Title
        
        function value = get.TitlePosition( obj )
            value = get( obj.UIContainer, 'TitlePosition' );
        end % get.TitlePosition
        
        function set.TitlePosition( obj, value )
            set( obj.UIContainer, 'TitlePosition', value );
            obj.redraw();
        end % set.TitlePosition
        
    end % accessor methods
    
    methods( Access = protected )
        
        function redraw( obj )
            %redraw  Redraw the layout, positioning the children
            
            if isempty( obj.UIContainer ) || ~ishandle( obj.UIContainer )
                return
            end
            
            % The selected one inherits the visibility of the layout and
            % fills the available space
            oldUnits = obj.Units;
            obj.Units = 'Pixels';
            pos = getpixelposition( obj );
            border = obj.BorderWidth+1+obj.Padding;
            x0 = border;
            y0 = border;
            w = pos(3) - 2*border;
            h = pos(4) - 2*border;
            if ~isempty( obj.Title )
                % Work out how much extra space to leave for the title
                oldFontUnits = get( obj.UIContainer, 'FontUnits' );
                set( obj.UIContainer, 'FontUnits', 'Pixels' );
                % Get the height of the title (in pixels)
                titleSize = get( obj.UIContainer, 'FontSize' );
                % Put the old units back
                set( obj.UIContainer, 'FontUnits', oldFontUnits );
                
                % Whether to move top or bottom depends on title
                % position
                if isempty( strfind( get( obj.UIContainer, 'TitlePosition' ), 'top' ) )
                    % Title at the bottom
                    h = h - titleSize;
                    y0 = y0 + titleSize;
                else
                    % Title at the top
                    h = h - titleSize;
                end
                
            end
            % Use the CardLayout function to put the right child onscreen
            obj.showSelectedChild( [x0 y0 w h] );
            obj.Units = oldUnits;
        end % redraw
        
        function onChildAdded( obj, source, eventData ) %#ok<INUSD>
            %onChildAdded: Callback that fires when a child is added to a container.
            % Select the new addition
            obj.SelectedChild = numel( obj.Children );
        end % onChildAdded
        
        function onChildRemoved( obj, source, eventData ) %#ok<INUSL>
            %onChildAdded: Callback that fires when a container child is destroyed or reparented.
            % If the missing child is the selected one, select something else
            if eventData.ChildIndex == obj.SelectedChild
                if isempty( obj.Children )
                    obj.SelectedChild = [];
                else
                    obj.SelectedChild = max( 1, obj.SelectedChild - 1 );
                end
            end
        end % onChildRemoved
        
        function onEnable( obj, source, eventData ) %#ok<INUSD>
            %onEnable  Callback that fires when the enable state is changed
            t = findall( obj.UIContainer, ...
                'Type', 'uicontrol', ...
                'HandleVisibility', 'off', ...
                'Parent', obj.UIContainer );
            set( t, 'Enable', obj.Enable );
        end % onChildRemoved
        
        function onBackgroundColorChanged( obj, source, eventData ) %#ok<INUSL>
            %onBackgroundColorChanged  Callback that fires when the container background color is changed
            %
            % We need to make the panel match the container background
            set( obj.UIContainer, 'BackgroundColor', eventData );
        end % onChildRemoved
        
        function onPanelColorChanged( obj, source, eventData ) %#ok<INUSL>
            % Colors have changed. This shouldn't require a redraw.
            set( obj.UIContainer, eventData.Property, eventData.Value );
        end % onPanelColorChanged
        
        function onPanelFontChanged( obj, source, eventData ) %#ok<INUSL>
            % Font has changed. Since the font size and shape affects the
            % space available for the contents, we need to redraw.
            set( obj.UIContainer, eventData.Property, eventData.Value );
            obj.redraw();
        end % onPanelFontChanged
        
    end % protected methods
    
end % classdef