www.gusucode.com > GUI的布局工具箱 > GUI的布局工具箱/GUI的布局工具箱/GUILayout-v1p10/+uiextras/Container.m
classdef Container < hgsetget %Container Container base class % % c = uiextras.Container() creates a new container object. Container % is an abstract class and can only be constructed as the first % actual of a descendent class. % % c = uiextras.Container(param,value,...) creates a new container % object and sets one or more property values. % % See also: uiextras.Box % uiextras.ButtonBox % uiextras.CardPanel % uiextras.Grid % Copyright 2009-2010 The MathWorks, Inc. % $Revision: 367 $ % $Date: 2011-02-10 16:25:22 +0000 (Thu, 10 Feb 2011) $ properties DeleteFcn % function to call when the layout is being deleted [function handle] end % Public properties properties( Dependent, Transient ) BackgroundColor % background color [r g b] BeingDeleted % is the object in the process of being deleted [on|off] Children % list of the children of the layout [handle array] Enable % allow interaction with the contents of this layout [on|off] Parent % handle of the parent container or figure [handle] Position % position [left bottom width height] Tag % tag [string] Type % the object type (class) [string] Units % position units [inches|centimeters|normalized|points|pixels|characters] Visible % is the layout visible on-screen [on|off] end % dependent properties properties( Access = protected, Hidden, Transient ) Listeners = cell( 0, 1 ) % array of listeners end % protected properties properties( SetAccess = private, GetAccess = protected, Hidden, Transient ) UIContainer % associated uicontainer end % read-only protected properties properties( Access = private, Hidden, Transient ) Children_ = zeros( 0, 1 ) % private copy of the children list ChildListeners = cell( 0, 2 ) % listeners for changes to children Enable_ = 'on' % private copy of the enabled state CurrentSize_ = [0 0] % private copy of the size end % private properties methods function obj = Container( varargin ) %Container Container base class constructor % % obj = Container(param,value,...) creates a new Container % object using the (optional) property values specified. This % may only be called by child classes. % Check that we're using the right graphics version if isHGUsingMATLABClasses() error( 'GUILayout:WrongHGVersion', 'Trying to run using double-handle MATLAB graphics against the new graphics system. Please re-install.' ); end % Find if parent has been supplied parent = uiextras.findArg( 'Parent', varargin{:} ); if isempty( parent ) parent = gcf(); end units = uiextras.findArg( 'Units', varargin{:} ); if isempty( units ) units = 'Normalized'; end % Create container args = { 'Parent', parent, ... 'Units', units, ... 'BorderType', 'none' }; obj.UIContainer = uipanel( args{:} ); % Set the background color obj.setPropertyFromDefault( 'BackgroundColor' ); % Tag it! set( obj.UIContainer, 'Tag', strrep( class( obj ), '.', ':' ) ); % Create listeners to resizing of container containerObj = handle( obj.UIContainer ); obj.Listeners{end+1,1} = handle.listener( containerObj, findprop( containerObj, 'PixelBounds' ), 'PropertyPostSet', @obj.onResized ); % Create listeners to addition of container children obj.Listeners{end+1,1} = handle.listener( containerObj, 'ObjectChildAdded', @obj.onChildAddedEvent ); % Watch out for the graphics being destroyed obj.Listeners{end+1,1} = handle.listener( containerObj, 'ObjectBeingDestroyed', @obj.onContainerBeingDestroyed ); % Store Container in container setappdata( obj.UIContainer, 'Container', obj ); end % constructor function container = double( obj ) %double Convert a container to an HG double handle. % % D = double(C) converts a container C to an HG handle D. container = obj.UIContainer; end % double function pos = getpixelposition( obj ) %getpixelposition get the absolute pixel position % % POS = GETPIXELPOSITION(C) gets the absolute position of the container C % within its parent window. The returned position is in pixels. pos = getpixelposition( obj.UIContainer ); end % getpixelposition function tf = isprop( obj, name ) %isprop does this object have the specified property % % TF = ISPROP(C,NAME) checks whether the object C has a % property named NAME. The result, TF, is true if the % property exists, false otherwise. tf = ismember( name, properties( obj ) ); end % isprop function p = ancestor(obj,varargin) %ancestor Get object ancestor % % P = ancestor(H,TYPE) returns the handle of the closest ancestor of h % that matches one of the types in TYPE, or empty if there is no matching % ancestor. TYPE may be a single string (single type) or cell array of % strings (types). If H is a vector of handles then P is a cell array the % same length as H and P{n} is the ancestor of H(n). If H is one of the % specified types then ancestor returns H. % % P = ANCESTOR(H,TYPE,'TOPLEVEL') finds the highest level ancestor of one % of the types in TYPE % % If H is not an Handle Graphics object, ANCESTOR returns empty. p = ancestor( obj.UIContainer, varargin{:} ); end %ancestor function delete( obj ) %delete destroy this layout % % If the user destroys the object, we *must* also remove any % graphics if ~isempty( obj.DeleteFcn ) uiextras.callCallback( obj.DeleteFcn, obj, [] ); end if ishandle( obj.UIContainer ) ... && ~strcmpi( get( obj.UIContainer, 'BeingDeleted' ), 'on' ) delete( obj.UIContainer ); end end % delete end % public methods methods function set.Position( obj, value ) set( obj.UIContainer, 'Position', value ); end % set.Position function value = get.Position( obj ) value = get( obj.UIContainer, 'Position' ); end % get.Position function set.Children( obj, value ) % Check oldChildren = obj.Children_; newChildren = value; [tf, loc] = ismember( oldChildren, newChildren ); if ~isequal( size( oldChildren ), size( newChildren ) ) || any( ~tf ) error( 'GUILayout:Container:InvalidPropertyValue', ... 'Property ''Children'' may only be set to a permutation of itself.' ) end % Set obj.Children_ = newChildren; % Reorder ChildListeners obj.ChildListeners(loc,:) = obj.ChildListeners; % Redraw obj.redraw(); end % set.Children function value = get.Children( obj ) value = obj.Children_; end % get.Children function set.Enable( obj, value ) % Check if ~ischar( value ) || ~ismember( lower( value ), {'on','off'} ) error( 'GUILayout:Container:InvalidPropertyValue', ... 'Property ''Enable'' may only be set to ''on'' or ''off''.' ) end % Apply value = lower( value ); % If we want to switch on but our parent is off, just store % in the app data. if strcmp( value, 'on' ) if isappdata( obj.Parent, 'Container' ) parentObj = getappdata( obj.Parent, 'Container' ); if strcmpi( parentObj.Enable, 'off' ) setappdata( obj.UIContainer, 'OldEnableState', value ); value = 'off'; end end end obj.Enable_ = value; % Apply to children ch = obj.Children_; for ii=1:numel( ch ) obj.helpSetChildEnable( ch(ii), obj.Enable_ ); end % Do the work obj.onEnable( obj, value ); end % set.Enable function value = get.Enable( obj ) value = obj.Enable_; end % get.Enable function set.Units( obj, value ) set( obj.UIContainer, 'Units', value ); end % set.Units function value = get.Units( obj ) value = get( obj.UIContainer, 'Units' ); end % get.Units function set.Parent( obj, value ) set( obj.UIContainer, 'Parent', double( value ) ); end % set.Parent function value = get.Parent( obj ) value = get( obj.UIContainer, 'Parent' ); end % get.Parent function set.Tag( obj, value ) set( obj.UIContainer, 'Tag', value ); end % set.Tag function value = get.Tag( obj ) value = get( obj.UIContainer, 'Tag' ); end % get.Tag function value = get.Type( obj ) value = class( obj ); end % get.Type function value = get.BeingDeleted( obj ) value = get( obj.UIContainer, 'BeingDeleted' ); end % get.BeingDeleted function set.Visible( obj, value ) set( obj.UIContainer, 'Visible', value ); end % set.Visible function value = get.Visible( obj ) value = get( obj.UIContainer, 'Visible' ); end % get.Visible function set.BackgroundColor( obj, value ) set( obj.UIContainer, 'BackgroundColor', value ); obj.onBackgroundColorChanged( obj, value ); end % set.BackgroundColor function value = get.BackgroundColor( obj ) value = get( obj.UIContainer, 'BackgroundColor' ); end % get.BackgroundColor end % accessor methods methods( Access = protected ) function onResized( obj, source, eventData ) %#ok<INUSD> %onResized Callback that fires when a container is resized. newSize = getpixelposition( obj ); newSize = newSize([3,4]); if any(newSize ~= obj.CurrentSize_) % Size has changed, so must redraw obj.CurrentSize_ = newSize; obj.redraw(); end end % onResized function onContainerBeingDestroyed( obj, source, eventData ) %#ok<INUSD> %onContainerBeingDestroyed Callback that fires when the container dies delete( obj ); end % onContainerBeingDestroyed function onChildAdded( obj, source, eventData ) %#ok<INUSD> %onChildAdded Callback that fires when a child is added to a container. obj.redraw(); end % onChildAdded function onChildRemoved( obj, source, eventData ) %#ok<INUSD> %onChildRemoved Callback that fires when a container child is destroyed or reparented. obj.redraw(); end % onChildRemoved function onBackgroundColorChanged( obj, source, eventData ) %#ok<INUSD,MANU> %onBackgroundColorChanged Callback that fires when the container background color is changed end % onChildRemoved function onEnable( obj, source, eventData ) %#ok<INUSD,MANU> %onEnable Callback that fires when the enable state is changed end % onChildRemoved function c = getValidChildren( obj ) %getValidChildren Return a list of only those children not being deleted c = obj.Children; c( strcmpi( get( c, 'BeingDeleted' ), 'on' ) ) = []; end % getValidChildren function repositionChild( obj, child, position ) %repositionChild adjust the position and visibility of a child if position(3)<=0 || position(4)<=0 % Not enough space, so move offscreen instead set( child, 'Position', [-100 -100 10 10] ); else % There's space, so make sure visibility is on % First determine whether to use "Position" or "OuterPosition" if isprop( child, 'ActivePositionProperty' ) propname = get( child, 'ActivePositionProperty' ); else propname = 'Position'; end % Now set the position in pixels, changing the units first if % necessary oldunits = get( child, 'Units' ); if strcmpi( oldunits, 'Pixels' ) set( child, propname, position ); else % Other units, so switch to pixels before setting set( child, 'Units', 'pixels' ); set( child, propname, position ); set( child, 'Units', oldunits ); end end end % repositionChild function setPropertyFromDefault( obj, propName ) %getPropertyDefault Retrieve a default property value. If the %value is not found in the parent or any of its ancestors the %supplied defValue is used. error( nargchk( 2, 2, nargin ) ); parent = get( obj.UIContainer, 'Parent' ); myClass = class(obj); if strncmp( myClass, 'uiextras.', 9 ) myClass = myClass(10:end); end defPropName = ['Default',myClass,propName]; % Getting the default will fail if the default does not exist % of has an invalid value. In that case we leave the current % value as it is. try obj.(propName) = uiextras.get( parent, defPropName ); catch err %#ok<NASGU> % Failed, so leave it alone end end % setPropertyFromDefault function helpSetChildEnable( ~, child, state ) % Set the enabled state of one child widget if strcmpi( get( child, 'Type' ), 'uipanel' ) % Might be another layout if isappdata( child, 'Container' ) child = getappdata( child, 'Container' ); else % Can't enable a panel child = []; end elseif isprop( child, 'Enable' ) % It supports enabling directly else % Doesn't support enabling child = []; end if ~isempty( child ) % We will use a piece of app data % to track the original state to ensure we don't % re-enable something that shouldn't be. if strcmpi( state, 'On' ) if isappdata( child, 'OldEnableState' ) set( child, 'Enable', getappdata( child, 'OldEnableState' ) ); rmappdata( child, 'OldEnableState' ); else set( child, 'Enable', 'on' ); end else if ~isappdata( child, 'OldEnableState' ) setappdata( child, 'OldEnableState', get( child, 'Enable' ) ); end set( child, 'Enable', 'off' ); end end end % helpSetChildEnable end % protected methods methods( Abstract = true, Access = protected ) redraw( obj ) end % abstract methods methods( Access = private, Sealed = true ) function onChildAddedEvent( obj, source, eventData ) %#ok<INUSL> %onChildAddedEvent Callback that fires when a child is added to a container. % Find child in Children child = eventData.Child; if ismember( child, obj.Children_ ) return % not *really* being added end % Only hook up internally if not a "hidden" child. if ~isprop( child, 'HandleVisibility' ) ... || strcmpi( get( child, 'HandleVisibility' ), 'off' ) return; end % We don't want to do anything to the panel title if isappdata( obj.UIContainer, 'PanelTitleCreate' ) ... && getappdata( obj.UIContainer, 'PanelTitleCreate' ) % This child is the panel label. Set its visibility off so % we don't see it again. set( child, 'HandleVisibility', 'off' ); return; end % We also need to ignore legends as they are positioned by % their associated axes. if isa( child, 'axes' ) && strcmpi( get( child, 'Tag' ), 'legend' ) return; end % Add element to Children obj.Children_(end+1,:) = child; % Add elements to ChildListeners. A bug in R2009a and % earlier means we have to be careful about this if isBeforeR2009b() obj.ChildListeners(end+1,:) = ... {handle.listener( child, 'ObjectBeingDestroyed', {@helpDeleteChild,obj} ), ... handle.listener( child, 'ObjectParentChanged', {@helpReparentChild,obj} )}; else obj.ChildListeners(end+1,:) = ... {handle.listener( child, 'ObjectBeingDestroyed', @obj.onChildBeingDestroyedEvent ), ... handle.listener( child, 'ObjectParentChanged', @obj.onChildParentChangedEvent )}; end % We are taking over management of position and will do it % in either pixel or normalized units. units = lower( get( child, 'Units' ) ); if ~ismember( units, {'pixels' ,'normalized'} ) set( child, 'Units', 'Pixels' ); end % If we are disabled, make sure the children are too if strcmpi( obj.Enable_, 'off' ) helpSetChildEnable( obj, child, obj.Enable_ ); end % Call onChildAdded eventData = uiextras.ChildEvent( child, numel( obj.Children_ ) ); obj.onChildAdded( obj, eventData ); end % onChildAddedEvent function onChildBeingDestroyedEvent( obj, source, eventData ) %#ok<INUSD> %onChildBeingDestroyedEvent Callback that fires when a container child is destroyed. % Find child in Children [dummy, loc] = ismember( source, obj.Children_ ); %#ok<ASGLU> % Remove element from Children obj.Children_(loc,:) = []; % Remove elements from ChildListeners obj.ChildListeners(loc,:) = []; % If we are in our death throes, don't start calling callbacks if ishandle( obj.UIContainer ) && ~strcmpi( get( obj.UIContainer, 'BeingDeleted' ), 'ON' ) % Call onChildRemoved eventData = uiextras.ChildEvent( source, loc ); obj.onChildRemoved( obj, eventData ); end end % onChildBeingDestroyedEvent function onChildParentChangedEvent( obj, source, eventData ) %onChildParentChangedEvent Callback that fires when a container child is reparented. if isempty( eventData.NewParent ) ... || eventData.NewParent == obj.UIContainer return % not being reparented *away* end % Find child in Children [dummy, loc] = ismember( source, obj.Children_ ); %#ok<ASGLU> % Remove element from Children obj.Children_(loc,:) = []; % Remove elements from ChildListeners obj.ChildListeners(loc,:) = []; % Call onChildRemoved eventData = uiextras.ChildEvent( source, loc ); obj.onChildRemoved( obj, eventData ); end % onChildParentChangedEvent end % private sealed methods end % classdef % ------------------------------------------------------------------------- % Helper functions to work around a bug in R2009a and earlier function ok = isBeforeR2009b() persistent matlabVersionDate; if isempty( matlabVersionDate ) v = ver( 'MATLAB' ); matlabVersionDate = datenum( v.Date ); % uiwait( msgbox( sprintf( 'Got MATLAB version date: %s', v.Date ) ) ) end ok = ( matlabVersionDate <= datenum( '15-Jan-2009', 'dd-mmm-yyyy' ) ); end function helpDeleteChild( src, evt, obj ) obj.onChildBeingDestroyedEvent( src, evt ); end % helpDeleteChild function helpReparentChild( src, evt, obj ) obj.onChildParentChangedEvent( src, evt ); end % helpReparentChild