www.gusucode.com > appdesigner工具箱matlab源码程序 > appdesigner/interface/+appdesigner/+internal/+componentadapterapi/VisualComponentAdapter.m

    classdef VisualComponentAdapter < appdesigner.internal.componentadapterapi.ComponentAdapter
    %
    % VisualComponentAdapter  Base class for all visual component adapters integrated
    %                         into the AppDesigner
    %
    % Copyright 2013-2016 The MathWorks, Inc.
    %
    
    properties (Abstract, SetAccess=protected, GetAccess=public)
        % an array of properties, where the order in the array determines
        % the order the properties must be set for Code Generation and
        % instantiating the design-time MCOS component.  Adapters that
        % do not have order specific properties will set this property to
        % be an empty cell array {}
        OrderSpecificProperties
        
        % the "Value" property of the component.  For example, a Lamp's
        % ValueProperty is 'Value' and a TextField's ValueProperty is
        % 'Text'.
        % Adapters that do not have a "Value" property will set this to
        % empty []
        ValueProperty
    end
    
    
    methods
        function obj = VisualComponentAdapter(varargin)
            % construct a visual component adapter
            % the first arg passed to the base class is 'true' indicating
            % it is a Visual Component
            obj@appdesigner.internal.componentadapterapi.ComponentAdapter(...,
                true, varargin{:});
        end
        
        % ---------------------------------------------------------------------
        % get the component Design Time default values
        % ---------------------------------------------------------------------
        function defaultValues = getComponentDesignTimeDefaults(obj)
            % return a struct of component properties and their
            % design-time default values.  To get the defaults the
            % design-time component is created without realizing the view
            
            defaultValues = ...
                matlab.ui.control.internal.view.getComponentDesignTimeDefaults(...
                obj);
        end
        
        % ---------------------------------------------------------------------
        % get the component run-time default values
        % ---------------------------------------------------------------------
        function defaultValues = getComponentRunTimeDefaults(obj)
            % return a struct of component properties and their
            % run-time default values
            
            % get the run time defaults for a component parented to a
            % uifigure
            parentFigure = uifigure('Visible', 'off');
            % delete the figure
            cf = onCleanup(@()delete(parentFigure));
            component = feval(obj.getComponentType(), ...
                'Parent', parentFigure);
            defaultValues = get(component);
            delete(component);
        end
        
        % ---------------------------------------------------------------------
        % Code Gen Method to return an array of property names, in the correct
        % order, as required by Code Gen
        % ---------------------------------------------------------------------
        function propertyNames = getCodeGenPropertyNames(obj, componentHandle)
            % The algorithm builds the property names the following way:
            %
            % - Explicit OrderSpecific properties
            %
            % - All other properties
            %
            % - Position
            %
            % - Value Property
            
            import appdesigner.internal.componentadapterapi.VisualComponentAdapter;
            
            % Get all properties as a struct and get the property names
            % properties as a starting point
            propertyValuesStruct = get(componentHandle);
            allProperties = fieldnames(propertyValuesStruct);
            
            % Filter out read-only properties
            mc = metaclass(componentHandle);
            % If SetAccess is not a string, it means only a list of classes
            % that have SetAccess to the property
            % Otherwise it would be 'private' or 'protected'.
            % In the metaclass, there's no Access information, only
            % SetAccess and GetAccess, and so only check SetAccess to see
            % if it's a readonly propery or not
            readOnlyPropertyIxs = find(arrayfun(@(x) ~ischar(x.SetAccess) || ~strcmp(x.SetAccess, 'public'), mc.PropertyList));
            readOnlyProperties = arrayfun(@(x) mc.PropertyList(x).Name, readOnlyPropertyIxs, 'UniformOutput', false);            
            allProperties = setdiff(allProperties, readOnlyProperties, 'stable');

            % Properties that are always ignored and are never set when
            % generating code
            %
            % Remove these from both the properties and order specific
            % properties
            ignoredProperties = {...
                'ComponentCode', ...
                'OuterPosition', ...
                'InnerPosition', ... % use Position only
                'Parent', ...
                'Children', ...
                'Label', ...
                'Tag', ...
                'Type', ...
                'UserData', ...
                'Serializable', ...
                };           
            
            % Get properties related to mode so some can be excluded
            [autoModeProperties, ~, manualModeProperties] = ...
                matlab.ui.control.internal.model.PropertyHandling.getModeProperties(propertyValuesStruct);
            
            % Rationale for excluding sets for specific mode properties:
            %
            % When a property is 'auto', then there is no need to generate
            % code for either corresponding sibling property nor the mode.
            %
            %     Ex: A Gauge with autocalculatd MajorTicks and
            %         MajorTicksMode = 'auto'
            %
            %     The generated code should not reflect either of these
            %     properties, as setting MajorTicks will result in the
            %     'mode' being flipped.  Setting MajorTicksMode to 'auto'
            %     would be redundant, as that is the default value for all
            %     'Mode' properties.
            %
            % When a property is 'manual', then there is no need to generate
            % code for the mode, and the mode property is excluded.
            %
            %     Ex: A Gauge with custom MajorTicks and MajorTicksMode =
            %         'manual'
            %
            %     The generated code should reflect only the MajorTicks
            %     being set.  Setting MajorTicks ensures the users custom
            %     value is honored, and will as a side effect flip the mode
            %     to manual.  The user does not need to see the mode being
            %     set to manual, as that is redundant as well as noisy.
            modePropertiesToIgnore = [autoModeProperties, manualModeProperties];
            
            if isempty(obj.OrderSpecificProperties)
                firstProperties = obj.OrderSpecificProperties;
            else
                % 'stable' preserves order (maintaining the user's order)
                %
                % otherwise, it would be alphabetical
                firstProperties = setdiff(obj.OrderSpecificProperties, ignoredProperties, 'stable');
            end
            
            % Determine the last properties, as row
            propertiesAtEnd = {'Position'};
            
            if ~isempty(obj.ValueProperty)
                % Specific property is specified, it will be last
                propertiesAtEnd{end+1} = obj.ValueProperty;
            end
            
            % Find all properties that are not in a specific location
            % Remove the mode properties from the list of
            % allOtherProperties
            allOtherProperties = setdiff(allProperties, ...
                [ignoredProperties, firstProperties, ...
                propertiesAtEnd, modePropertiesToIgnore], 'stable');
            
            % Remove mode properties from first and end so they are not
            % automatically written.  Doing these set diffs on a smaller set
            % is more efficient than operating on the set.
            if ~isempty(firstProperties)
                firstProperties = setdiff(firstProperties, modePropertiesToIgnore, 'stable');
            end
            if ~isempty(propertiesAtEnd)
                propertiesAtEnd = setdiff(propertiesAtEnd, modePropertiesToIgnore, 'stable');
            end
            
            
            % Create the master list
            propertyNames = [...
                firstProperties, ...
                allOtherProperties', ...
                propertiesAtEnd, ...
                ];
            
        end
        
        % ---------------------------------------------------------------------
        % Code Gen Method that has the logic of whether the code should
        % show for that property.  This takes into consideration if the
        % property is a mode sibling or if it is a default.
        % ---------------------------------------------------------------------
        function showPropertyCode = shouldShowPropertySetCode(obj,isPropertyModeSibling, componentHandle,propertyName, defaultComponent)
            
            showPropertyCode = false;
            
           %  Sometimes an app file contains a dynamic property on the model
           %  unknown to the release.  In this situation,  code will try to be generated
           % for it causing an exception and code gen to stop.
           
           % The software now catches the exception  and return false for the 
           % "shouldShowPropertySetCode() method.  
           
           % Need to do a try-catch here, to allow isDefault() method
           % inside the try-catch to be executed becuase some components
           % overload it
            try
                
                % If the propertyName has a Mode sibling, and the Mode
                % sibling value is manual, always generate the code.
                if isPropertyModeSibling
                    if strcmp(componentHandle.([propertyName, 'Mode']), 'manual')
                        % Only show a mode sibling property if the Mode is
                        % manual.  When a mode sibling is 'auto' the code
                        % should not appear.
                        showPropertyCode = true;
                    end
                else
                                    
                    % Property should only be written if value is different
                    % than the default value
                    isDefaultValue = obj.isDefault(componentHandle, propertyName, defaultComponent);
                    
                    if ~isDefaultValue
                        
                        showPropertyCode = true;
                    end
                end
                
            catch err
                % if the propertyName is not on the default component, then it is an unknown dynamic property
                % so don't try to generate code         
                if ~isfield(defaultComponent,propertyName)
                    % If not a field, make sure showPropertyCode is set to false,
                    showPropertyCode = false;
                else
                    % otherwise its an unexpected error so rethrow it         
                    rethrow(err);
                end

           end
            
        end
        
        % ---------------------------------------------------------------------
        % Code Gen Method to return a status of whether the value
        % represents the default value of the component. If isDefault
        % returns true, no code will be generated for that property
        % ---------------------------------------------------------------------
        
        function isDefaultValue = isDefault(obj,componentHandle,propertyName, defaultComponent)
            % ISDEFAULT - Returns a true or false status based on whether
            % the value of the component corresponding to the propertyName
            % inputted is the default value.  If the value returned is
            % true, then the code for that property will not be displayed
            % in the code at all
            % This method is overwritten by components where the
            % propertyName isn't on the component itself.. for instance
            % TitleString on the axes
            
            value = componentHandle.(propertyName);
            
            defaultValue = defaultComponent.(propertyName);
            
            % If the current value and the default value of the
            % component are the same,isDefaultValue should be true
            % If both properties are empty, but different data
            % types or sizes, this should be interpretted as that
            % they are the same.
            isDefaultValue = isequal(value, defaultValue) || ...
                ... Special case, both values are empty, but different dimensions
                all([isempty(value), isempty(defaultValue)]);
            
        end
        
        % ---------------------------------------------------------------------
        % Code Gen Method to return an array of property names, in the correct
        % order, as required by Code Gen
        % ---------------------------------------------------------------------
        
        function codeSnippet = getCodeGenPropertySet(obj,component,propertyName, codeName, parentCodeName)
            % GETCODEGENPROPERTYSET - Generates a line of code that would
            % set the property designated in the input propertyName.
            %
            % Components may want to dictate how specific properties appear
            % in the code the default implementation may look something
            % like this:
            % app.CodeName.Property = 'string';
            %
            % Where the axes may want the option to set the property
            % someother way:
            % convenienceFunction(app)
            %
            % One example is for the title of an Axes
            % Default: app.Axes2.Title.String = 'Title of my App';
            % Alternate: title('Title of my App');
            
            % Requirements/ Best practices
            % * The codeSnippet should be a string with no new lines.
            % * Leading white space and trailing new line is not required
            % and not recommended. The code will be formatted for spacing
            % requirements in the ComponentInitCode object
            % * It is best practice to include a trailing semi-colon in the
            % codeSnippet if the code you are producing would display
            % anything
            % * Your code should not produce mlint warnings, the
            % codeSnippet is directly user facing.
            % *
            
            value = component.(propertyName);
            codeSnippet = ...
                appdesigner.internal.codegeneration.ComponentCodeGenerator.generateStringForPropertySegment(...
                codeName, propertyName, value);
            
        end
        
        
        % ---------------------------------------------------------------------
        % When a component is added on the client, the client sends a child-added
        % event to MATLAB so the corresponding MCOS object can created.  Data
        % with this event is a set of properties that are set on the new MCOS
        % object.  This method takes as input a structure of those component
        % properties that are to be set.  Here is the opportunity for the
        % adapters to tweek them if needed.
        % ---------------------------------------------------------------------
        function processPropertiesStruct = processPropertiesToSet(obj,propertiesStruct)
            % the default implementation of this method is to reorder the
            % properties as defined by the OrderSpecificProperties
            % and Value properties of the adapter.
            
            % create an array of properties to be set in a specific order.
            % The "ValueProperty" is always to be last in the array
            if isempty(obj.ValueProperty)
                lastProperty = {};
            else
                lastProperty = obj.ValueProperty;
            end
            
            % need to remove the OrderSpecificProperties and ValueProperty
            % from the propertiesStruct passed in and then add them back in
            % a specific order
            
            % first get the property names in the structure
            propertyNames = fieldnames(propertiesStruct);
            
            % remove the orderedProperties and lastProperty from the cell array of property names
            trimmedPropertyList = setdiff(propertyNames,[obj.OrderSpecificProperties, lastProperty] );
            
            % add them back in propertyNames cell array in a specific order
            updatedPropertyList = [obj.OrderSpecificProperties, trimmedPropertyList', lastProperty];
            
            % only apply properties that belong to both the changed
            % properties and the list of properties to generate code
            propertiesOfInterestToModel = intersect(fieldnames(propertiesStruct), updatedPropertyList);
            
            % order the fields in the propertiesStruct according to the new
            % updatedPropertyList
            processPropertiesStruct = orderfields(propertiesStruct, propertiesOfInterestToModel);
        end
    end
    
    methods(Static)
        
        % ---------------------------------------------------------------------
        % Code Gen Method to return a status of whether the propertyName
        % entered has a 'Mode' sibling.  If the propertyName is YTick, and
        % YTickMode exists, the status will be true. propertyList is
        % expected to be a cell array of strings
        % ---------------------------------------------------------------------
        
        function isModeSibling = isModeSibling(propertyList, component)
            % ISMODESIBLING - Returns an array of booleans the same size as
            % propertyList.  It returns 1 if the propertyName + 'Mode'
            % property exists on the component.
            
            isModeSibling = false(size(propertyList));
            componentProperties = properties(component);
            
            for index = 1:numel(propertyList)
                
                propertyName = [propertyList{index}, 'Mode'];
                
                % Use 'properties' method, as that only introspects public
                % properties
                %
                % HG Objects have Hidden underlying mode properties which
                % are not relevant code generation.  Code Generation is
                % only concerned with properties that have an
                % auto-calculating behavior, like Ticks + TicksMode.
                isModeSibling(index) = any(strcmp(propertyName, componentProperties));
            end
            
        end
        
    end
    
end