www.gusucode.com > mbcguitools 工具箱 matlab 源码程序 > mbcguitools/+mbcgui/+motion/MotionManager.m
classdef MotionManager < matlab.mixin.SetGet & matlab.mixin.Copyable & matlab.mixin.Heterogeneous %mbcgui.motion.MotionManager class % mbcgui.motion.MotionManager properties: % Figure - Property is of type 'MATLAB array' % Mode - Property is of type 'string' % Enable - Property is of type 'on/off' % MouseMoveFcn - Property is of type 'MATLAB callback' % MouseInFcn - Property is of type 'MATLAB callback' % MouseOutFcn - Property is of type 'MATLAB callback' % EnableTree - Property is of type 'bool' % Region - Property is of type 'rect' % UseExternalRef - Property is of type 'on/off' % ExternalRef - Property is of type 'MATLAB array' % % mbcgui.motion.MotionManager methods: % AttachToFigure - Attach Motion Manager to figure % ExecuteMovement - Execute MotionManager callbacks % FindManager - Find the existing MotionManager for a figure or panel % RecoverMotion - Re-apply motion function to figure % RegisterManager - Register a MotionManager % RegisterSingleManager - Register a MotionManager % UnregisterManager - Unregister a MotionManager % UnregisterSingleManager - Unregister a MotionManager % clearRegions - Emit MouseOut event to all sub-Managers % doUpdate - Force an update of mouse position tracking % enableManager - determine whether to enable manager % getChildren - Return the registered sub-managers % getEnabled - Return vector of enabled values for a vector of Managers % getXmax - Return vector of Xmax values from a vector of MotionManagers % getXmin - Return vector of Xmin values from a vector of MotionManagers % getYmax - Return vector of Ymax values from a vector of MotionManagers % getYmin - Return vector of Ymin values from a vector of MotionManagers % setCallback - Set up new callback function % setEnable - Finalise new enable value % setEnableTree - Finalise new EnableTree setting % setExternalRef - Finalise new external reference setting % setExternalRefFlag - Finalise new external reference setting % setRegion - Finalise new region value % Copyright 2000-2016 The MathWorks, Inc. and Ford Global Technologies, Inc. properties %FIGURE Property is of type 'MATLAB array' Figure %MODE Property is of type 'string' Mode = 'Multi'; %ENABLETREE Property is of type 'bool' EnableTree = true; %REGION Property is of type 'rect' Region = [ 0, 0, 1, 1 ]; %USEEXTERNALREF Property is of type 'on/off' UseExternalRef = 'off'; %EXTERNALREF Property is of type 'MATLAB array' ExternalRef % UserEnabled = 'on'; end properties(SetAccess=protected) %Parent parent motionmanager. Parent end properties (Access=protected) %SINGLELISTENER Property is of type 'handle' SingleListener %MULTILISTENER Property is of type 'handle vector' MultiListener %INREGION Property is of type 'MATLAB array' InRegion %HMOVEFCNLIST Property is of type 'handle' hMoveFcnList %HINFCNLIST Property is of type 'handle' hInFcnList %HOUTFCNLIST Property is of type 'handle' hOutFcnList %XMIN Property is of type 'double' Xmin=0; %XMAX Property is of type 'double' Xmax=1; %YMIN Property is of type 'double' Ymin=0; %YMAX Property is of type 'double' Ymax=1; %EXTERNALREFFLAG Property is of type 'bool' ExternalRefFlag = false; %EXTERNALREFLISTENER Property is of type 'MATLAB array' ExternalRefListener %FIGLISTENER Property is of type 'MATLAB array' FigListener %MOUSEMOVELISTENER Property is of type 'handle' MouseMoveListener %MOUSEOUTLISTENER Property is of type 'handle' MouseOutListener end properties (SetAccess=protected,SetObservable) %ENABLE Property is of type 'on/off'. A motion manager is %enabled if UserEnabled, all its parent motion managers are %enabled and the state of the container/figure allows an active %motion manager. Use UserEnabled to manually disabled a %MotionManager Enable = 'on'; end properties (SetObservable) %MOUSEMOVEFCN Property is of type 'MATLAB callback' MouseMoveFcn %MOUSEINFCN Property is of type 'MATLAB callback' MouseInFcn %MOUSEOUTFCN Property is of type 'MATLAB callback' MouseOutFcn end events (NotifyAccess=protected) MouseMove MouseIn MouseOut end % events methods % constructor block function obj = MotionManager(varargin) %MOTIONMANAGER Window Motion management class % OBJ = MOTIONMANAGER(PROP, VALUE, ...) if nargin set(obj,varargin{:}); end obj.MouseMoveListener = event.listener(obj, 'MouseMove',@obj.ExecuteMovement); obj.MouseOutListener = event.listener(obj, 'MouseOut',@obj.clearRegions); end % MotionManager end % constructor block methods function set.Enable(obj,value) obj.Enable = value; obj.setEnable; end function set.UserEnabled(obj,value) obj.UserEnabled = value; obj.setEnable; end function set.MouseMoveFcn(obj,value) obj.MouseMoveFcn = value; obj.setCallback('MouseMove', 'hMoveFcnList', value); end function set.MouseInFcn(obj,value) obj.MouseInFcn = value; obj.setCallback('MouseIn', 'hInFcnList', value); end function set.MouseOutFcn(obj,value) obj.MouseOutFcn = value; obj.setCallback('MouseOut', 'hOutFcnList', value); end function set.EnableTree(obj,value) obj.EnableTree = value; obj.setEnableTree(value); end function set.Region(obj,value) obj.Region = value; obj.setRegion(value); end function set.UseExternalRef(obj,value) obj.UseExternalRef = value; obj.setExternalRefFlag(value); end function set.ExternalRef(obj,value) obj.ExternalRef = value; obj.setExternalRef(value) end end % set and get functions methods % public methods %---------------------------------------- function AttachToFigure(obj, figh) %ATTACHTOFIGURE Attach Motion Manager to figure % ATTACHTOFIGURE(OBJ, FIG) attaches OBJ to the figure FIG. if isempty(figh) obj.RecoverMotion(@i_motion, false); return end % check the figure is valid, and doesn't already have a manager if ~isgraphics(figh,'figure') error(message('mbc:xregGui:MotionManager:InvalidHandle')); end if ~isprop(figh,'MotionManagerStore') mbcgui.hgclassesutil.addprop(figh, 'MotionManagerStore', 'Hidden', true); end % If we get to here, then the figure should be ok for attaching to set(figh,'MotionManagerStore',obj); % attach to motion function set(figh,'WindowButtonMotionFcn', {@i_motion, obj}); obj.Figure = figh; % Connect to Figure's control hook % addChildren(xregfigurehook( figh ),obj); % These listeners keep control of situations when someone else nabs the % WindowButtonMotionFcn The logic is a bit convoluted, in order to get % round some current UDD oddities The second two listeners actually hide my % MotionFcn when someone tries to get it: needed to make the class work % with some MATLAB Gui Tools like Zoom, Rotate3D obj.FigListener = { ... event.proplistener(figh, figh.findprop('WindowButtonMotionFcn'), 'PostSet', mbcutils.callback(@i_recovermotionfcn, obj)), ... event.proplistener(figh, figh.findprop('WindowButtonMotionFcn'), 'PreGet', mbcutils.callback(@i_hidemotionfcn, obj)), ... event.proplistener(figh, figh.findprop('Visible'), 'PostSet', @obj.onSetVisible) ... }; obj.Enable = mbconoff(enableManager(obj)); end % AttachToFigure %---------------------------------------- function ExecuteMovement(obj,~, evt) %EXECUTEMOVEMENT Execute MotionManager callbacks % EXECUTEMOVEMENT(OBJ, EVENTDATA) is the function that responds to a % MouseMove event on the object. Sub-Managers are checked to see whether % the mouse has moved into or out of their region and events are sent % accordingly. if strcmp(obj.Enable, 'on') cp = evt.CurrentPoint; if strcmpi(obj.Mode, 'multi') % need to check x, y coords with Listeners list = obj.MultiListener; if ~isempty(list) % Check for dead handles and remove them if ~all(isvalid(list)) % remove dead MM's alive_ind = isvalid(list); list = list(alive_ind); obj.MultiListener = list; obj.InRegion = obj.InRegion(alive_ind); end Oldreg = obj.InRegion; if length(Oldreg)~=length(list) % This condition should never happen but has been known to. % Reset the inreg to match correctly - may cause graphical % glitches. Oldreg = false(size(list)); end if ~isempty(list) en = getEnabled(list); else en = false(0); end % x-direction xmn= getXmin(list); xmx= getXmax(list); ymn= getYmin(list); ymx= getYmax(list); PlayersToExecute = en & ... (cp(1)>=xmn) & ... (cp(1)<=xmx) & ... (cp(2)>=ymn) & ... (cp(2)<=ymx); % Look for region in/out shifts delt = PlayersToExecute - Oldreg; if any(delt) for n = find(delt==-1) % mouse out notify(list(n), 'MouseOut'); end for n = find(delt==1) % mouse in notify(list(n), 'MouseIn'); end end if any(PlayersToExecute) for n = find(PlayersToExecute) notify(list(n), 'MouseMove', evt); end end obj.InRegion = PlayersToExecute; end else if ~isempty(obj.SingleListener) obj.SingleListener.notify('MouseMove', evt); end end end end % ExecuteMovement %---------------------------------------- function RecoverMotion(obj, fcnhndl, KILLPROP) %RECOVERMOTION Re-apply motion function to figure % RECOVERMOTION(FUNCTION_HANDLE) if nargin<3 KILLPROP = false; end if KILLPROP % remove the motion function very temporarily mbcgui.hgclassesutil.setListenerEnabled(obj.FigListener{1}, false); set(obj.Figure, 'WindowButtonMotionFcn', 'MotionManager(gcbf,''reconnect'');'); mbcgui.hgclassesutil.setListenerEnabled(obj.FigListener{1}, true); else mbcgui.hgclassesutil.setListenerEnabled(obj.FigListener{2}, false); wbmf = get(obj.Figure, 'WindowButtonMotionFcn'); mbcgui.hgclassesutil.setListenerEnabled(obj.FigListener{2}, true); if ~isempty(wbmf) mbcgui.hgclassesutil.setListenerEnabled(obj.FigListener{2}, false) else mbcgui.hgclassesutil.setListenerEnabled(obj.FigListener{1}, false) set(obj.Figure, 'WindowButtonMotionFcn',{fcnhndl,obj}); mbcgui.hgclassesutil.setListenerEnabled(obj.FigListener{1}, true); mbcgui.hgclassesutil.setListenerEnabled(obj.FigListener{2}, true); end end end % RecoverMotion %---------------------------------------- function RegisterManager(obj, pl, DoEvents) %REGISTERMANAGER Register a MotionManager % REGISTERMANAGER(OBJ, MANAGER, DOEVENTS) registers MANAGER as a % sub-region of the MotionManager OBJ. The boolean flag DOEVENTS controls % whether the new manager should be checked to see whether the mouse is in % the region. if ~all(isa(pl,'mbcgui.motion.MotionManager')) error(message('mbc:xregGui:MotionManager:InvalidObject')); end % append new manager to list of multi ones if isempty(obj.MultiListener) obj.MultiListener = pl; pl.Parent = obj; inreg = false; else % check for dead MM's first if ~all(isvalid(obj.MultiListener)) % remove dead MM's alive_ind = isvalid(obj.MultiListener); obj.MultiListener = obj.MultiListener(alive_ind); obj.InRegion = obj.InRegion(alive_ind); end % add new MM if ~any(pl==obj.MultiListener) obj.MultiListener = [obj.MultiListener pl]; pl.Parent = obj; inreg = [obj.InRegion false]; else return end end if nargin<3 DoEvents = true; end % Check new player for being currently active if ~isempty(obj.Figure) ploc = get(0,'PointerLocation'); cp = get(obj.Figure,'Position'); cp = ploc - cp(1:2) + [1 1]; if ~isempty(cp) inreg(end) = (cp(1)>=getXmin(pl) ... && cp(1)<=getXmax(pl) ... && cp(2)>=getYmin(pl) ... && cp(2)<=getYmax(pl)); if inreg(end) && DoEvents % fire MouseIn and MouseMove Events pl.notify('MouseIn'); pl.notify('MouseMove', mbcgui.motion.MotionEvent(cp)); end end end obj.InRegion = inreg; pl.Figure = obj.Figure; pl.Enable = obj.Enable; end % RegisterManager %---------------------------------------- function RegisterSingleManager(obj, pl) %REGISTERSINGLEMANAGER Register a MotionManager % REGISTERSINGLEMANAGER(OBJ, MANAGER) registers MANAGER as the single % sub-region of the MotionManager OBJ. if ~isa(pl,'mbcgui.motion.MotionManager') error(message('mbc:xregGui:MotionManager:InvalidObject1')); end % Save Manager as the single-usage one obj.SingleListener = pl; pl.Parent = obj; pl.Figure = obj.Figure; end % RegisterSingleManager %---------------------------------------- function UnregisterManager(obj, pl, sendclear) %UNREGISTERMANAGER Unregister a MotionManager % UNREGISTERMANAGER(OBJ, MANAGER) removes MANAGER from the list of % sub-managers of OBJ. if nargin<3 sendclear = true; end if ~isa(pl,'mbcgui.motion.MotionManager') error(message('mbc:xregGui:MotionManager:InvalidObject2')); end if ~isempty(obj.Figure) && ~strcmp(obj.Figure.BeingDeleted,'on') if ~all(isvalid(obj.MultiListener)) % remove dead MM's alive_ind = isvalid(obj.MultiListener); obj.MultiListener = obj.MultiListener(alive_ind); obj.InRegion = obj.InRegion(alive_ind); end % remove specified pl ind = (obj.MultiListener==pl); if any(ind) inreg = obj.InRegion(ind); pl.Parent = []; % remove player from list obj.MultiListener = obj.MultiListener(~ind); obj.InRegion = obj.InRegion(~ind); if any(inreg) && sendclear % send a MouseOut event pl.notify('MouseOut'); end end end end % UnregisterManager %---------------------------------------- function UnregisterSingleManager(obj, pl) %UNREGISTERSINGLEMANAGER Unregister a MotionManager % UNREGISTERSINGLEMANAGER(OBJ, MANAGER) removes MANAGER as the single % child manager of OBJ. if ~isa(pl,'mbcgui.motion.MotionManager') error(message('mbc:xregGui:MotionManager:InvalidObject3')); end if ~isempty(obj.Figure) && ~strcmp(obj.Figure.BeingDeleted,'on') % clear player as the single-usage one if ~isempty(obj.SingleListener) if ~isvalid(obj.SingleListener) || pl==obj.SingleListener obj.SingleListener= []; pl.Parent = []; end end end end % UnregisterSingleManager %---------------------------------------- function clearRegions(obj, ~,~) %CLEARREGIONS Emit MouseOut event to all sub-Managers % CLEARREGIONS(OBJ) sends a MouseOut event on all sub-Managers and marks % the mouse as being in none of the regions if any(obj.InRegion) for n = find(obj.InRegion) notify(obj.MultiListener(n), 'MouseOut'); end obj.InRegion = false(size(obj.InRegion)); end end % clearRegions %---------------------------------------- function doUpdate(obj) %DOUPDATE Force an update of mouse position tracking % DOUPDATE(OBJ) sends a mouse position update down the MotionManager tree. % Get main MotionManager for the figure since this is the only coordinate % system we know the correct location for MainMM = MotionManager(obj.Figure); pl = get(0,'PointerLocation'); cp = get(obj.Figure,'Position'); cp = pl - cp(1:2) + [1 1]; notify(MainMM,'MouseMove', mbcgui.motion.MotionEvent(cp)); end % doUpdate %---------------------------------------- function en = enableManager(obj) %enableManager determine whether to enable manager % en = enableManager(obj) % figure is visible en = strcmp(obj.Figure.Visible,'on'); end % enableManager %---------------------------------------- function ch=getChildren(mm) %GETCHILDREN Return the registered sub-managers % CH=getChildren(mm) returns a vector of handles to the registered % sub-managers of a motion manager. ch = mm.MultiListener; end % getChildren %---------------------------------------- function setCallback(obj, eventName, prop, cb) %SETCALLBACK Set up new callback function % SETCALLBACK(OBJ, EVENT, PROP, CALLBACK) if isempty(cb) L = []; else if iscell(cb) cb = mbcutils.callback(cb{:}); else cb = mbcutils.callback(cb); end L = event.listener(obj, eventName, cb); L.Enabled = strcmp(obj.Enable,'on'); end set(obj, prop, L); end % setCallback %---------------------------------------- function setEnable(obj) %SETENABLE Finalise new enable value % SETENABLE(OBJ) % set callback enable status enval = strcmp(obj.Enable,'on') && strcmp(obj.UserEnabled,'on'); if ~isempty(obj.hMoveFcnList) obj.hMoveFcnList.Enabled = enval; end if ~isempty(obj.hInFcnList) obj.hInFcnList.Enabled = enval; end if ~isempty(obj.hOutFcnList) obj.hOutFcnList.Enabled = enval; end if ~isempty(obj.MultiListener) % propogate enable down if enval for i=1:length(obj.MultiListener) % enable depending on child state set(obj.MultiListener(i),'Enable',mbconoff(enableManager(obj.MultiListener(i)))); end else % turn all children off [obj.MultiListener.Enable]= deal(mbconoff(enval)); end end end % setEnable %---------------------------------------- function setEnableTree(obj, enval) %SETENABLETREE Finalise new EnableTree setting % SETENABLETREE(OBJ, ENABLEVAL) % Pass on enabletree settting to the listener which fires the mousemove cascade if ~isempty(obj.MouseMoveListener) obj.MouseMoveListener.Enabled = enval; end end % setEnableTree %---------------------------------------- function setExternalRef(obj, ref, addlistener) %SETEXTERNALREF Finalise new external reference setting % SETEXTERNALREF(OBJ, REF, ADDLISTENER) if nargin<3 addlistener = true; end if addlistener && ~isempty(ref) % Attach listener to the object's position if isgraphics(ref) % HG if isgraphics(ref, 'axes') % always listen to position on an axes obj.ExternalRefListener = event.proplistener(ref, ... ref.findprop('Position'), 'PostSet', mbcutils.callback(@i_getpos, obj)); else obj.ExternalRefListener = mbcgui.hgclassesutil.positionlistener(... ref, ... mbcutils.callback(@i_getpos, obj), ... 'PostSet'); end elseif ishandle(ref) % UDD widget obj.ExternalRefListener = handle.listener(ref, ... ref.findprop('Position'), 'PropertyPostSet', {@i_getpos, obj}); else % MCOS obj.ExternalRefListener = event.proplistener(ref, ... findprop(ref, 'Position'), 'PostSet', mbcutils.callback(@i_getpos, obj)); end end if obj.ExternalRefFlag pos = get(ref, 'Position'); if ~isempty(pos) set(obj, ... 'Xmin', pos(1), ... 'Xmax', pos(1) + pos(3) - 1, ... 'Ymin', pos(2), ... 'Ymax', pos(2) + pos(4) - 1 ... ) end end end % setExternalRef %---------------------------------------- function setExternalRefFlag(obj, refsetting) %SETEXTERNALREFFLAG Finalise new external reference setting % SETEXTERNALREFFLAG(OBJ, REFSETTING) if strcmp(refsetting,'on') obj.ExternalRefFlag = true; % update min/max from externalref obj.setExternalRef(obj.ExternalRef); else obj.ExternalRefFlag = false; % update min/max from region obj.setRegion; end end % setExternalRefFlag %---------------------------------------- function setRegion(obj, region) %SETREGION Finalise new region value % SETREGION(OBJ, REGION) % Grab min and max values from the region if ~obj.ExternalRefFlag set(obj, ... 'Xmin', region(1), ... 'Xmax', region(1) + region(3) - 1, ... 'Ymin', region(2), ... 'Ymax', region(2) + region(4) - 1 ... ) end end % setRegion end % public methods methods (Sealed) %---------------------------------------- function en = getEnabled(hvect) %GETENABLED Return vector of enabled values for a vector of Managers % enVect = getEnabled(Hvect) returns a vector of logical values indicating % whether the MotionManagers are enabled. en = {hvect.Enable}; en = reshape(strcmp(en,'on'),size(hvect)); end % getEnabled %---------------------------------------- function xm = getXmax(hvect) %GETXMAX Return vector of Xmax values from a vector of MotionManagers % XmaxVect = GETXMAX(Hvect) returns a vector of maximum X values for the % vector of MotionManager handles Hvect. xm = {hvect.Xmax}; xm = reshape([xm{:}],size(hvect)); end % getXmax %---------------------------------------- function xm = getXmin(hvect) %GETXMIN Return vector of Xmin values from a vector of MotionManagers % XminVect = GETXMIN(Hvect) returns a vector of minimum X values for the % vector of MotionManager handles Hvect. xm = {hvect.Xmin}; xm = reshape([xm{:}],size(hvect)); end % getXmin %---------------------------------------- function ym = getYmax(hvect) %GETYMAX Return vector of Ymax values from a vector of MotionManagers % YmaxVect = GETYMAX(Hvect) returns a vector of maximum Y values for the % vector of MotionManager handles Hvect. ym = {hvect.Ymax}; ym = reshape([ym{:}],size(hvect)); end % getYmax %---------------------------------------- function ym = getYmin(hvect) %GETYMIN Return vector of Ymin values from a vector of MotionManagers % YminVect = GETYMIN(Hvect) returns a vector of minimum Y values for the % vector of MotionManager handles Hvect. ym = {hvect.Ymin}; ym = reshape([ym{:}],size(hvect)); end % getYmin function OK = eq(A,B) OK = eq@matlab.mixin.SetGet(A,B); end function OK = ne(A,B) OK = ne@matlab.mixin.SetGet(A,B); end function set(obj,varargin) set@matlab.mixin.SetGet(obj,varargin{:}); end function out = get(obj,prop) out = get@matlab.mixin.SetGet(obj,prop); end end methods (Static) %---------------------------------------- function hout=FindManager(figh) %FINDMANAGER Find the existing MotionManager for a figure or panel % M=obj.FindManager(FIG) returns the current manager for a figure, or % empty if one doesn't exist. try hout = figh.MotionManagerStore; catch hout = []; end end % FindManager end methods (Access=protected) function onSetVisible(M,~, evt) M.Enable = mbconoff(strcmp(get(evt.AffectedObject, 'Visible'),'on')); end % onSetVisible function OK = isParentEnabled(M) OK = isempty(M.Parent) || strcmp(M.Parent.Enable,'on'); end end end % classdef function i_motion(~, ~, obj) if isa(obj,'mbcgui.motion.MotionManager') obj.doUpdate; end end % i_motion function i_recovermotionfcn(~,~,obj) obj.RecoverMotion(@i_motion, false); end % i_recovermotionfcn function i_hidemotionfcn(~,~,obj) obj.RecoverMotion([], true); end % i_hidemotionfcn function i_getpos(~, ~, obj) obj.setExternalRef(obj.ExternalRef, false); end % i_getpos