www.gusucode.com > appdesigner工具箱matlab源码程序 > appdesigner/+appdesigner/+internal/+application/AppDesignerWindowController.m

    classdef AppDesignerWindowController < handle
    % AppDesignerWindowController manages starting and closing App Designer window
    %
    %   This class will start App Designer browser window, controlling the 
    %   lifetime of the browser window
    %   1) Build the parameters to start browser with specified connection
    %   and BrowserController
    %   2) Listen to PeerModelManager rootSet event to reponse to App
    %   Designer client fully started
    %   3) Listen to PeerModelManager rootUnset event to close browser and
    %   do clean up for client being closed
    %   4) Listen to WindowClosingCallback to handle browser closing
    %   request
    %   5) Listen to MATLABClosingCallback to handle MATLAB closing request
    %   6) Listen to 'ObjectBeingDestroyed' of BrowserController to handle
    %   browser process exits unexpectedly
    
    %   Copyright 2015-2016 The MathWorks, Inc.
    
    properties (Access = public)
        % the private browser target, default value is CEF
        BrowserControllerFactory = appdesservices.internal.peermodel.BrowserControllerFactory.CEF;
        
        % connection object to start the connector
        Connection        
    end
    
    properties (Access = private)
        % the App Designer model.  It is the model in "model = appdesigner"
        AppDesignerModel
        
        % the BrowserController manage to start/close browser
        BrowserController
        
        % the MATLAB side PeerModelManager
        PeerModelManager
        
        % listeners on the peerModelManager's rootset and rootUnset events
        % so the AppDesigner objects can be initialized and cleaned up appropriately
        PeerModelRootSetListener
        PeerModelRootUnsetListener
        
        % Listener on the root peer node's peerEvent to handle AppDesigner closed
        % by exitting MATLAB, like hitting "X" button of MATLAB
		AppDesignerPeerEventListener
    end
    
    methods (Access = public)
        function obj = AppDesignerWindowController(peerModelManager, appDesignerModel, connection)
            
            obj.PeerModelManager = peerModelManager;
            % listen to when the client's root peer node has been created
            obj.PeerModelRootSetListener = addlistener(obj.PeerModelManager, ...
                'rootSet', @(src,event)obj.handlePeerModelRootSet(event));
            
            obj.AppDesignerModel = appDesignerModel;
            
            % Connection object to manage the URL for staring App Designer
            obj.Connection = connection;
            
        end
        
        function startBrowser(obj, queryParams, browserControllerFactory)
            % STARTBROWSER start App Designer client browser
            % 
            %   queryParams : struct containing query parameters for URL
            %   browserControllerFactory: optional, browser for launching
            %   App Designer
            
            % The browserControllerFactory argument is optional which 
            % specify the browser to launch
            % if not provided, the default would be webwindow
            if nargin == 3
                obj.BrowserControllerFactory = browserControllerFactory;
            end
            
            % Build the query string
            queryFields = fieldnames(queryParams);
            queryStrings = cellfun(...
                @(f)sprintf('%s=%s', f, appdesigner.internal.application.encodeURIForJS(queryParams.(f))),...
                queryFields, 'UniformOutput', false);
            queryString = strjoin(queryStrings, '&');    
            
            % ensure we have a fresh URL
            obj.Connection.refresh(queryString);
           
            % set initial Browser State and launch the client
            initialBrowserState.Title = message('MATLAB:appdesigner:appdesigner:AppDesigner').getString();
            initialBrowserState.URL = obj.Connection.AbsoluteUrlPath;
            
            % Pass preference group to browser controller to save browser
            % window postion/state when closing, to restore when launching
            % App Designer next time
            initialBrowserState.PrefGroup = 'appdesigner';
            
            obj.BrowserController = obj.BrowserControllerFactory.launch(initialBrowserState);
            
            % When the browser is launched, the AppDesigner 
            % registers a close listener on the browser.
            % Then when the browser is closed, the delete()
            % method in this class is called which cleans up the 
            % browser instance.
            % However, if when launching AppDesigner, the user closes 
            % MATLAB very quickly, there is a chance the AppDesigner 
            % client did not have a chance to notify server side to register
            % the listener. If this is the case, there will be asyncio 
            % errors on the command as MATLAB is exiting because AppDesigner 
            % is still holding on to the reference to the browser.
            %
            % To avoid these errors, a CustomWindowClosingCallback on the
            % browser is installed that will perform the cleanup
            % in the situation described above.   
            obj.BrowserController.WindowClosingCallback = @(browserObj, event)delete(obj);
            
            % When CEF process exits unexpectedly, like being killed
            % from Task Manager, clean up App Designer. If this happens,
            % CEF would have no chance to call WindowClosingCallback
            obj.BrowserController.addlistener('ObjectBeingDestroyed', ...
                    @(source, event)delete(obj));
        end
        
        function bringToFront(obj)
            obj.BrowserController.bringToFront();            
        end
        
        function delete(obj)
            % cleanup the browser
            if ~isempty(obj.BrowserController) && isvalid(obj.BrowserController)
                % only startBrower() being called, BrowserController will
                % be created.
                delete(obj.BrowserController);            
            end
            
            % cleanup the listeners on the rootset, rootunset events
            if ~isempty(obj.PeerModelRootUnsetListener)
                % Because ClientCloseListener is set when client fully
                % opened, need to check before deleting
                delete(obj.PeerModelRootUnsetListener);
                
                % Set to empty after deleting, otherwise it would be a
                % value of deleted or invalid object when calling
                % delete() if the user hit "X" before completing
                % initialization
                obj.PeerModelRootUnsetListener = [];                
            end
            delete(obj.PeerModelRootSetListener);
            
            % cleanup the listener on the peerEvent event
            if ~isempty(obj.AppDesignerPeerEventListener)
                % PeerModelRootUnsetListener is setup when AppDesigner client is
                % fully initialized. If the user hit "X" before completing
                % initialization, PeerModelRootUnsetListener would be invalid.
                % So need to check it before deleting.
                delete(obj.AppDesignerPeerEventListener);
                % Set to empty after deleting, otherwise it would be a
                % value of deleted or invalid object when calling
                % delete() if the user hit "X" before completing
                % initialization
                obj.AppDesignerPeerEventListener = [];
            end
        end
    end
    
    methods (Access = private)
        function handlePeerModelRootSet(obj,event)
            % When PeerModelManager's root is set, the App Designer is started.
            % process the App Designer being fully started
            peerNode = event.getTarget();
            
            % Listen to peer event from client side to handle 'appDesignerClosed'
            % which is fired by exiting MATLAB
            obj.AppDesignerPeerEventListener = addlistener(peerNode, ...
                'peerEvent', @(src,event)obj.handleBrowserClosedByMATLAB(event));
            
            % listen to when the client's root peer node has been destroyed
            if obj.BrowserController.isCloseEventSupported()
                % only listen to rootUnset if the browser supports Close
                % event, otherwise refreshing webpage will cause the
                % window closed
                obj.PeerModelRootUnsetListener = addlistener(obj.PeerModelManager, ...
                    'rootUnset',@(src,event)obj.handlePeerModelRootUnset(event));
            end
            
            % the browser is intialized properly so set the 
            % WindowClosingCallback to handleBrowserRequestToClose to
            % let AppDesignerWindowController react to rootUnset to do
            % closing AppDesigner
            obj.BrowserController.WindowClosingCallback = @(browserObj, event)obj.handleBrowserRequestToClose(event);

            % When MATLAB is closed, AppDesigner will be closed directly 
            % without a chance to handle dirty apps. In order to give
            % users oppurtunity to save dirty apps, handle
            % MATLABClosing event from webwindow.
            % Only set this callback when browser is initialized
            % properly, otherwise client side Javascript can't react to this event
            obj.BrowserController.MATLABClosingCallback = @(browserObj, event)obj.handleMATLABRequestToClose(event);            
        end
        
        function handlePeerModelRootUnset(obj, ~)
           % When root node is unset of the PeerModelManager, the App Designer 
           % is closed. and so process the App Designer being closed
           
           delete(obj);
        end
        
        function handleBrowserClosedByMATLAB(obj, event)
            % process the browser being closed by exiting MATLAB
            
            % Get all the event data's names
            eventDataHashMap = event.getData();                        
            
            % Error Checking
            assert(eventDataHashMap.containsKey('Name'), ...
                'The event data is malformed.  It does not contain a ''Name'' field.');
            
            eventDataStruct = appdesservices.internal.peermodel.convertJavaMapToStruct(eventDataHashMap);
            % appDesignerClosedByExitingMATLAB peer event from client side
            if strcmp(eventDataStruct.Name, 'appDesignerClosedByExitingMATLAB')
                delete(obj);

                % At the end try to exit MATLAB because AppDesigner is closed by
                % MATLABRequestToClose event
                exit;                
            end
        end
        
        function handleBrowserRequestToClose(obj, ~)
            % Handle browser window closed event with requesting AppDesigner to close
            
            % Send BrowserRequestToClose event to client to let AppDesigner
            % have a chance to handle dirty apps
            obj.sendRequestToCloseEventToClient('BrowserRequestToClose');
        end
        
        function handleMATLABRequestToClose(obj, ~)
            % Handle MATLABClosing event with requesting AppDesigner to close
            
            % Send MATLABRequestToClose event to client to let AppDesigner
            % have a chance to handle dirty apps
            obj.sendRequestToCloseEventToClient('MATLABRequestToClose');
        end
        
        function sendRequestToCloseEventToClient(obj, eventName)
            % Bring AppDesigner to front and send peerEvent to client to
            % request to close
            
            % Bring AppDesigner to front to let users to answer save or not
            % if there's any dirty apps
            obj.BrowserController.bringToFront();
            
            % Send "request to close event" to client to let AppDesigner
            % have a chance to handle dirty apps
            obj.AppDesignerModel.getControllerHandle().ProxyView.sendEventToClient(eventName, {});
        end
        
    end    
end