Ryan Joseph via fpc-pascal wrote:

Adriaan, what was your idea you had in mind when you brought this up?

Well, to give you an idea, here is an example (somewhat simplified for clarity). I am currently porting MacApp to 64-bit. MacApp currently has

        TView = class( TEventHandler)

However, I have a TQDGraphPort class with QuickDraw emulation. For the QuickDraw emulation to work with current application code, TQDGraphPort methods (like TQDGraphPort.MoveTo, TQDGraphPort.LineTo, etcetera) have to be methods (or be in the namespace) of TView. So, I would really need

        TView = class( TQDGraphPort, TEventHandler)

The TEventHandler methods have to be methods (or be in the namespace) of TView, because existing application code overrides the methods of TEventHandler in objects inheriting from TView. This mechanism needs to continue working.

Other classes also inherit from TEventHandler: TDocument, TPrintHandler and 
TApplication.

I solved this now by defining four event-handling interfaces: IIdleHandler, IMenuHandler, IKeyHandler, IMouseHandler, all inheriting from IEventHandler, Something like:

    IEventHandler                           =
      interface
          [ 'IEventHandler']

           function GetNextEventObj         : TObject;
      end;

    IKeyHandler                             =
      interface
        ( IEventHandler)
          [ 'IKeyHandler' ]

           function DoKeyCommand
              (     theChar                 : char;
                    theKeyCode              : Int16;
                var theEventInfo            : EventInfo): TCommand;

           function DoCommandKey
              (     theChar                 : char;
                var theEventInfo            : EventInfo): TCommand;
      end;

etcetera. The event-handling classes can now import the interfaces:

    TApplication = class( TRefCountObj, IKeyHandler, iMenuHandler, IIdleHandler)
    TDocument = class( TDBFile, IKeyHandler, iMenuHandler, IIdleHandler)
    TView = class( TMacAppGraphPort, IKeyHandler, IMenuHandler, IIdleHandler, 
IMouseHandler)

etcetera. The disadvantage of this approach is that for example a default DoKeyCommand must be written three times, for TApplication, TDocument and TView, where the code for TDocument.DoKeyCommand and TView.DoKeyCommand is identical.

Identical code is clumsy. Therefore my wish that a helper object could somehow be put in the namespace of the importing object, including the ability to override the imported methods. I hope that answers you question.

I admit however that with some clever interface programming, the duplicated code can be reduced in the above case to a single line. As follows:

 function GetObjEventHandler
        (     theObj                  : TObject): IEventHandler;
      var
        theEventHandler               : IEventHandler;
    begin
      if ( theObj <> nil) and theObj.GetInterface
       ( IEventHandler, theEventHandler)
        then GetObjEventHandler       := theEventHandler
        else GetObjEventHandler       := nil
    end;

 function GetObjKeyHandler
        (     theObj                  : TObject): IKeyHandler;
      var
        theKeyHandler                 : IKeyHandler;
    begin
      if ( theObj <> nil) and theObj.GetInterface
       ( IKeyHandler, theKeyHandler)
        then GetObjKeyHandler         := theKeyHandler
        else GetObjKeyHandler         := nil
    end;

 function NextKeyCommand
        (     theObj                  : TObject;
              theChar                 : char;
              theKeyCode              : Int16;
          var theEventInfo            : EventInfo): TCommand;
      var
        theEventHandler               : IEventHandler;
        theKeyHandler                 : IKeyHandler;
    begin
      theKeyHandler                   := nil;
      while ( theObj <> nil) and ( theKeyHandler = nil) do
      begin
        theEventHandler               := GetObjEventHandler
          ( theObj);
        if theEventHandler = nil
          then theObj                 := nil
          else theObj                 := theEventHandler.GetNextEventObj;
        theKeyHandler                 := GetObjKeyHandler
          ( theObj)
      end;
      if theKeyHandler <> nil
        then NextKeyCommand           := theKeyHandler.DoKeyCommand
           ( theChar, theKeyCode, theEventInfo)
        else NextKeyCommand           := nil
    end;

 function TDocument.DoKeyCommand
        (     theChar                 : char;
              theKeyCode              : Int16;
          var theEventInfo            : EventInfo): TCommand;
    begin
      DoKeyCommand                    := NextKeyCommand
        ( self, theChar, theKeyCode, theEventInfo)
    end;

 function TView.DoKeyCommand
        (     theChar                 : char;
              theKeyCode              : Int16;
          var theEventInfo            : EventInfo): TCommand;
    begin
      DoKeyCommand                    := NextKeyCommand
        ( self, theChar, theKeyCode, theEventInfo)
    end;

Note that TDocument.DoKeyCommand and TView.DoKeyCommand are identical, but not larger than one function call.

Regards,

Adriaan van Os

_______________________________________________
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply via email to