Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2012-04

TImage - Getting image properties in 'canvas space'

46 messages · 9 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Reimar Grabowski

Hi,

I load a TLazIntfImage from disk, manipulate it and show it on my form in a TImage. Center, Proportional and Stretch properties of the TImage are set to true. Now I want to draw some overlays onto it (drawing to the canvas in the OnPaint event). Therefor I obviously need the origin and dimensions of the loaded image in 'canvas space', but I did not find a way to retrieve this information. Do I have to calculate them myself or can the TImage give me the needed information?

Greetings
R.

Reply by email

Mattias Gaertner
to Reimar Grabowski

Reimar Grabowski hat am 16. April 2012 um 16:42 geschrieben:

Show quoted text 10 lines
Hi, I load a TLazIntfImage from disk, manipulate it and show it on my form in a TImage. Center, Proportional and Stretch properties of the TImage are set to true. Now I want to draw some overlays onto it (drawing to the canvas in the OnPaint event). Therefor I obviously need the origin and dimensions of the loaded image in 'canvas space', but I did not find a way to retrieve this information. Do I have to calculate them myself or can the TImage give me the needed information?

The image is painted to the area defined by the protected method DestRect. Maybe this can be made public.

At the moment you have to descend your own TImage class to access it or write a hack class (e.g. via a class helper).

Mattias
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20120416/7f6c81c6/attachment.html>

Reply by email

Reimar Grabowski
to Mattias Gaertner
The image is painted to the area defined by the protected method DestRect. Maybe this can be made public.

Sounds like a good idea to me, as it is very useful information.

At the moment you have to descend your own TImage class to access it or write a hack class (e.g. via a class helper).

Class helpers are a new concept to me and IMHO quite a strange one. I can understand why you call this a hack but at least it works. Thanks. It looks like they aren't fully supported by the code tools with regard to code completion.

R.

Reply by email

Marc Weustink
to Mattias Gaertner: Reimar Grabowski hat am 16. April 2012 um 16:42 geschrieben: The image is painted to the…
Mattias Gaertner wrote 21 lines
Reimar Grabowski hat am 16. April 2012 um 16:42 geschrieben: > Hi, > > I load a TLazIntfImage from disk, manipulate it and show it on my form in a TImage. Center, Proportional and Stretch properties of the TImage are set to true. > Now I want to draw some overlays onto it (drawing to the canvas in the OnPaint event). Therefor I obviously need the origin and dimensions of the loaded image in 'canvas space', but I did not find a way to retrieve this information. Do I have to calculate them myself or can the TImage give me the needed information? The image is painted to the area defined by the protected method DestRect. Maybe this can be made public. At the moment you have to descend your own TImage class to access it or write a hack class (e.g. via a class helper).

TImage is not meant to draw to. If you want to custom paint your image, use a TPaintbox

A TImage is for display only.

Marc

Reply by email

Sven Barth
to Reimar Grabowski: On Mon, 16 Apr 2012 18:12:32 +0200 (CEST) Sounds like a good idea to me, as it is very…
Reimar Grabowski wrote
> At the moment you have to descend your own TImage class to access it or write a > hack class (e.g. via a class helper). Class helpers are a new concept to me and IMHO quite a strange one. I can understand why you call this a hack but at least it works. Thanks. It looks like they aren't fully supported by the code tools with regard to code completion.

I personally don't consider them strange, but that might be, because I implemented them in FPC ^^

Their main purpose should be considered to add functionality to classes/records without inheriting from them (which might not always be possible, because records can not inherit or you have e.g. the class hierarchy of the LCL which you can't trivially change).

Regards,
Sven

Reply by email

Mattias Gaertner
to Marc Weustink: TImage is not meant to draw to. If you want to custom paint your image, use a TPaintbox A…
Show quoted text 27 lines
Mattias Gaertner wrote: > > Reimar Grabowski hat am 16. April 2012 um 16:42 > geschrieben: > > > Hi, > > > > I load a TLazIntfImage from disk, manipulate it and show it on my > form in a TImage. Center, Proportional and Stretch properties of the > TImage are set to true. > > Now I want to draw some overlays onto it (drawing to the canvas in > the OnPaint event). Therefor I obviously need the origin and dimensions > of the loaded image in 'canvas space', but I did not find a way to > retrieve this information. Do I have to calculate them myself or can the > TImage give me the needed information? > > The image is painted to the area defined by the protected method DestRect. > > Maybe this can be made public. > > At the moment you have to descend your own TImage class to access it or > write a hack class (e.g. via a class helper). TImage is not meant to draw to. If you want to custom paint your image, use a TPaintbox A TImage is for display only.

TImage has a published OnPaint event. Painting some overlays makes sense. And for this you need to know where the image is on the canvas.

Mattias

Reply by email

Reimar Grabowski
to Sven Barth: I personally don't consider them strange, but that might be, because I implemented them…
I personally don't consider them strange, but that might be, because I implemented them in FPC ^^

Considering that they were a hackish workaround to marry Delphis VCL and .NET and Delphi users were warned not to use them because they should not be used outside the VCL now seeing them in FPC is strange.

Their main purpose should be considered to add functionality to classes/records without inheriting from them (which might not always be possible, because records can not inherit or you have e.g. the class hierarchy of the LCL which you can't trivially change).

That's the information I gathered from reading different articles about them but there were also problems mentioned because class helpers can hide each other (only one may be active). So I'd rather not use them in code which is meant for other people to use as they may cause problems by just adding a unit (2 helpers for the same type in 2 different units). Correct me if my assumptions are wrong.
For the task at hand they work, nonetheless they feel quite hackish to me.

R.

Reply by email

Reimar Grabowski
to Mattias Gaertner: On Tue, 17 Apr 2012 12:12:21 +0200 TImage has a published OnPaint event. Painting some…
Show quoted text 10 lines
On Tue, 17 Apr 2012 12:12:21 +0200 Marc Weustink wrote: > TImage is not meant to draw to. If you want to custom paint your image, > use a TPaintbox > > A TImage is for display only. TImage has a published OnPaint event. Painting some overlays makes sense. And for this you need to know where the image is on the canvas.

This.
And to me it looks like this method is officially supported by Lazarus as you can see here:
http://wiki.freepascal.org/Developing_with_Graphics#Painting_on_the_volatile_visual_area_of_the_TImage

R.

Reply by email

Marc Weustink
to Mattias Gaertner: On Tue, 17 Apr 2012 12:12:21 +0200 TImage has a published OnPaint event. Painting some…
Mattias Gaertner wrote 33 lines
On Tue, 17 Apr 2012 12:12:21 +0200 Marc Weustink wrote: > Mattias Gaertner wrote: >> >> Reimar Grabowski hat am 16. April 2012 um 16:42 >> geschrieben: >> >> > Hi, >> > >> > I load a TLazIntfImage from disk, manipulate it and show it on my >> form in a TImage. Center, Proportional and Stretch properties of the >> TImage are set to true. >> > Now I want to draw some overlays onto it (drawing to the canvas in >> the OnPaint event). Therefor I obviously need the origin and dimensions >> of the loaded image in 'canvas space', but I did not find a way to >> retrieve this information. Do I have to calculate them myself or can the >> TImage give me the needed information? >> >> The image is painted to the area defined by the protected method DestRect. >> >> Maybe this can be made public. >> >> At the moment you have to descend your own TImage class to access it or >> write a hack class (e.g. via a class helper). > > TImage is not meant to draw to. If you want to custom paint your image, > use a TPaintbox > > A TImage is for display only. TImage has a published OnPaint event. Painting some overlays makes sense. And for this you need to know where the image is on the canvas.

Who has added this ? Delphi has no OnPaint event.

Yes, if we have an event, we should provide a way to paint. But to me this is a can of worms, since stretching is handled internally while the user event draws on the canvas, independent of the stretching. If you ask me, I wouldn't have added it.

Marc

Reply by email

Sven Barth
to Reimar Grabowski: On Tue, 17 Apr 2012 12:26:13 +0200 Considering that they were a hackish workaround to…
Reimar Grabowski wrote 6 lines
On Tue, 17 Apr 2012 12:26:13 +0200 Sven Barth wrote: > I personally don't consider them strange, but that might be, because I > implemented them in FPC ^^ Considering that they were a hackish workaround to marry Delphis VCL and .NET and Delphi users were warned not to use them because they should not be used outside the VCL now seeing them in FPC is strange.

Yes, they were described as "only used inside the VCL", but if I consider that class helpers are used without big care on one of Germany's biggest Delphi forums (Delphi-PRAXiS), I've come to the conclusion that they are a rather nice feature to have and once I have the time (and have stabilized the generics and worked on my Native NT port) I'll revisit the helpers to improve them more (e.g. helpers for objects and interfaces, having multiple helpers active for one type and maybe even support for helpers for primitive types...). So I personally consider helpers a fully fledged feature of FPC. I don't care whether they are "don't use" in Delphi or not. In FPC you can consider them as "use them if you see a need for them", the same as for the old TP style objects, operator overloading, generics, etc.

Show quoted text 7 lines
> Their main purpose should be considered to add functionality to > classes/records without inheriting from them (which might not always be > possible, because records can not inherit or you have e.g. the class > hierarchy of the LCL which you can't trivially change). That's the information I gathered from reading different articles about them but there were also problems mentioned because class helpers can hide each other (only one may be active). So I'd rather not use them in code which is meant for other people to use as they may cause problems by just adding a unit (2 helpers for the same type in 2 different units). Correct me if my assumptions are wrong.

Yes, helpers currently have that restriction, but as I wrote above I'd like to remove that (at least optionally by using a modeswitch).

For the task at hand they work, nonetheless they feel quite hackish to me.

Well... class helpers are at least an officially supported hackish approach unlike other forms of making a protected member visible.

Regards,
Sven

Reply by email

Mattias Gaertner
to Sven Barth
[...] Well... class helpers are at least an officially supported hackish approach unlike other forms of making a protected member visible.

I wonder what protected is still good for when you can use class helpers to access protected members from anywhere.

Mattias

Reply by email

Sven Barth
to Reimar Grabowski
Reimar Grabowski wrote 6 lines
On Tue, 17 Apr 2012 19:46:08 +0200 Sven Barth wrote: > Well... class helpers are at least an officially supported hackish > approach unlike other forms of making a protected member visible. Deriving my own class is certainly more work (as in more to type) but it is officially supported and I fail to see the hackish part.

If you are able to derive it, like in the case of TImage, that's indeed the best solution. But there are cases in code where you have some kind of "hack class" that makes a protected member or (yes, I've seen such) even a private one accessible (by utilizing the memory layout of the class) without making an instance of that class.

For your example this would be (I don't know whether this exact example would work, but I've seen similar working ones):

type
   TMyHackImage = class(TImage)
   public
     function DestRect: TRect;
   end;
var
   MyImage: TImage;
   r: TRect;
begin
   // let's suppose that MyImage contains a TImage instance
   r := TMyHackImage(MyImage).DestRect
end;

Regards,
Sven

Reply by email

Reimar Grabowski
to Sven Barth
Show quoted text 13 lines
type TMyHackImage = class(TImage) public function DestRect: TRect; end; var MyImage: TImage; r: TRect; begin // let's suppose that MyImage contains a TImage instance r := TMyHackImage(MyImage).DestRect end;

How should that work?
What does the DestRec function look like?
IMHO to access DestRec TMyHackImage must be derived from TCustomImage and in that case the cast won't work.

R.

Reply by email

Sven Barth
to Reimar Grabowski
Reimar Grabowski wrote 19 lines
On Tue, 17 Apr 2012 22:19:45 +0200 Sven Barth wrote: > type > TMyHackImage = class(TImage) > public > function DestRect: TRect; > end; > > var > MyImage: TImage; > r: TRect; > begin > // let's suppose that MyImage contains a TImage instance > r := TMyHackImage(MyImage).DestRect > end; How should that work? What does the DestRec function look like? IMHO to access DestRec TMyHackImage must be derived from TCustomImage and in that case the cast won't work.

Yes, sorry. I've now tested the following example which is different from your original problem, but it illustrates the hacky mechanism:

=== source begin ===

program hacktest;
{$mode objfpc}
uses
   Classes;
type
   TMyStrings = class(TStringList)
   end;
var
   s: TStringList;
begin
   s := TStringList.Create;
   s.Add('Test');
   Writeln(TMyStrings(s).GetCount); // "GetCount" is protected in
"TStringList"
end.

=== source end ===

Regards,
Sven

Reply by email

Hans-Peter Diettrich
to Mattias Gaertner: On Tue, 17 Apr 2012 19:46:08 +0200 I wonder what protected is still good for when you can…
Show quoted text 9 lines
On Tue, 17 Apr 2012 19:46:08 +0200 Sven Barth wrote: > [...] > Well... class helpers are at least an officially supported hackish > approach unlike other forms of making a protected member visible. I wonder what protected is still good for when you can use class helpers to access protected members from anywhere.

The VCL design often hides properties (as protected), which should be accessible by user code, under certain circumstances. The classic workaround for such cases was the declaration of a dummy class, derived from an existing VCL class, what allows to access the protected members of the *base* class in that unit. Such situations now can be handled by reusable class helpers, with better encapsulation of the added features; they can allow access to *selected* protected members of the class, still hiding other protected members.

The downside of the Delphi model: it's hard to determine, which class helper is *currently* used in some code, because the unit order in the uses clause(s) is involved hereby. That's why I would add my class helpers to every unit in which they are used, to eliminate that uncertainty.

DoDi

Reply by email

Hans-Peter Diettrich
to Reimar Grabowski: On Tue, 17 Apr 2012 19:46:08 +0200 Deriving my own class is certainly more work (as in…
Show quoted text 7 lines
On Tue, 17 Apr 2012 19:46:08 +0200 Sven Barth wrote: > Well... class helpers are at least an officially supported hackish > approach unlike other forms of making a protected member visible. Deriving my own class is certainly more work (as in more to type) but it is officially supported and I fail to see the hackish part.

That's okay when you *can* create instances of your classes. But often a user has to deal with an object of an *predefined* type, so that deriving a class; which is never instantiated; is of no use.

And that's where the hack enters the scene: The mere declaration of a derived class, or a typecast of a given object into a derived class, allows the user to access members which are not accessible in the actual (dynamic) type of that object.

It should be mentioned that class helpers do not solve all problems, e.g. when a TStrings parameter is passed to a subroutine, in which it's *assumed* that it actually is of type TStringList. Then a typecast of that parameter is required, before a class helper for TStringList can be used with it. Failing to use an *checked* typecast here, what's common practice <sigh>, may result in any kind of trouble.

DoDi

Reply by email

Hans-Peter Diettrich
to Reimar Grabowski: On Tue, 17 Apr 2012 22:19:45 +0200 How should that work? What does the DestRec function…
Show quoted text 18 lines
On Tue, 17 Apr 2012 22:19:45 +0200 Sven Barth wrote: > type > TMyHackImage = class(TImage) > public > function DestRect: TRect; > end; > > var > MyImage: TImage; > r: TRect; > begin > // let's suppose that MyImage contains a TImage instance > r := TMyHackImage(MyImage).DestRect > end; How should that work? What does the DestRec function look like?

You're right, above code should not work. The DestRect function has to be omitted from above declaration, so that the DestRect of the base class is used.

The elevation of visibility only works for *properties*, i.e.

   public
     property DestRect;

should work. But often (depending on the compiler) the mere declaration of an empty derived class allows to access protected members of the base class.

DoDi

Reply by email

Mattias Gaertner
to Marc Weustink: Who has added this ? Delphi has no OnPaint event. Yes, if we have an event, we should…
Show quoted text 41 lines
Mattias Gaertner wrote: > On Tue, 17 Apr 2012 12:12:21 +0200 > Marc Weustink wrote: > >> Mattias Gaertner wrote: >>> >>> Reimar Grabowski hat am 16. April 2012 um 16:42 >>> geschrieben: >>> >>> > Hi, >>> > >>> > I load a TLazIntfImage from disk, manipulate it and show it on my >>> form in a TImage. Center, Proportional and Stretch properties of the >>> TImage are set to true. >>> > Now I want to draw some overlays onto it (drawing to the canvas in >>> the OnPaint event). Therefor I obviously need the origin and dimensions >>> of the loaded image in 'canvas space', but I did not find a way to >>> retrieve this information. Do I have to calculate them myself or can the >>> TImage give me the needed information? >>> >>> The image is painted to the area defined by the protected method DestRect. >>> >>> Maybe this can be made public. >>> >>> At the moment you have to descend your own TImage class to access it or >>> write a hack class (e.g. via a class helper). >> >> TImage is not meant to draw to. If you want to custom paint your image, >> use a TPaintbox >> >> A TImage is for display only. > > TImage has a published OnPaint event. Painting some overlays makes > sense. And for this you need to know where the image is on the canvas. Who has added this ? Delphi has no OnPaint event. Yes, if we have an event, we should provide a way to paint. But to me this is a can of worms, since stretching is handled internally while the user event draws on the canvas, independent of the stretching. If you ask me, I wouldn't have added it.

I don't see many worms in the can, so I made it public. It's a useful feature that costs us not much.

Mattias

Reply by email

Reimar Grabowski
to Hans-Peter Diettrich: You're right, above code should not work. The DestRect function has to be omitted from…
But often (depending on the compiler) the mere declaration of an empty derived class allows to access protected members of the base class.

What does depending on the compiler mean? The compiler is obviously FPC. If it is really true that you can access protected members in that way (like Svens message suggests) I consider this a bug. Like Mattias said, protected is of no real use in that case and we can just declare all non private members public.

R.

Reply by email

Reimar Grabowski
to Hans-Peter Diettrich: That's okay when you *can* create instances of your classes. But often a user has to deal…
It should be mentioned that class helpers do not solve all problems,

Not being able to access a member that you should not access is not a problem. It's what visibility in OOP is ment to do. It is by design that you cannot access that member. Having an official way to do this is IMHO bad. People will use it. And not just in special situations. They will use it as integral part of their software design and that is not a good idea.

R.

Reply by email

Alberto Narduzzi
to Reimar Grabowski: On Wed, 18 Apr 2012 09:58:56 +0200 What does depending on the compiler mean? The compiler…
Show quoted text 6 lines
> But often (depending on the compiler) the mere declaration > of an empty derived class allows to access protected members of the base > class. What does depending on the compiler mean? The compiler is obviously FPC. If it is really true that you can access protected members in that way (like Svens message suggests) I consider this a bug. Like Mattias said, protected is of no real use in that case and we can just declare all non private members public.

I guess this must be considered a bug. You can indeed access protected members of parent classes, but only from within the (your derived) class members themselves, not from the outer world.
At least this I believe to e the meaning of having protected (me and my friends only), together with public (everybody) and private (me and only me).
Let alone published, there for IDE designers only.

Cheers, A.

Reply by email

Alberto Narduzzi
to Reimar Grabowski: On Wed, 18 Apr 2012 09:50:16 +0200 Not being able to access a member that you should not…
> It should be mentioned that class helpers do not solve all problems, Not being able to access a member that you should not access is not a problem. It's what visibility in OOP is ment to do. It is by design that you cannot access that member. Having an official way to do this is IMHO bad. People will use it. And not just in special situations. They will use it as integral part of their software design and that is not a good idea.

totally agree.

An interface to a class (or its concept, anyway) is there for a reason. And you should adhere to. I may also say that if you need to access a protected, or private for what is worth, member of a (library...) class, then you should revise your code... because either you're using the wrong class, or your problem can be solved in a different, possibly more elegant, and surely more OO compliant way ;-)

Cheers, A.

Reply by email

Graeme Geldenhuys
to Reimar Grabowski: On Wed, 18 Apr 2012 09:58:56 +0200 What does depending on the compiler mean? The compiler…
Reimar Grabowski wrote
What does depending on the compiler mean? The compiler is obviously FPC. If it is really true that you can access protected members in that way (like Svens message suggests) I consider this a bug. Like Mattias said, protected is of no real use in that case and we can just declare all non private members public.

Framework and Component developers cannot foresee all possible use-cases. So they might have thought protected is fine. I have numerous cases where I had to use the "class helper hack" (before the class helper or friend class features was introduced into FPC or Delphi). This doesn't mean protected is of no use any more, or that all protected methods should now become public, but it is often the case where one method needs to be accessed.

If you really want concrete examples, I can search my code for where I use the "class helper hack".

Regards, ? - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net

Reply by email

Graeme Geldenhuys
to Alberto Narduzzi: totally agree. An interface to a class (or its concept, anyway) is there for a reason.…
Alberto Narduzzi wrote 7 lines
An interface to a class (or its concept, anyway) is there for a reason. And you should adhere to. I may also say that if you need to access a protected, or private for what is worth, member of a (library...) class, then you should revise your code... because either you're using the wrong class, or your problem can be solved in a different, possibly more elegant, and surely more OO compliant way ;-)

I totally disagree... :)

There are valid use cases where developers need access to protected methods. Here is a case in point (and I have many other examples too).

I implemented the Model-GUI-Mediator (MGM) design pattern to make any standard components (non-db) "object aware" for the tiOPF persistence framework. MGM allows me to bind object data to any standard components, and have two-way updating, without the need to create even more custom or descendant GUI components (this replaces the need for DB-aware components). Some of the mediators are implemented in a generic fashion, using base classes of GUI components so that that same mediator could be used for any descendant classes of that component (view).

eg: Below is some code for a TCustomEdit mediator. This mediators applies to any TCustomEdit descendants - and there are many of those. In the case below, we needed access to the ReadOnly property of TCustomEdit, but it was defined as protected (which was the correct thing to do for TCustomXXX components). In the code below, View is the component instance we are working with and is defined as TCustomEdit type - thus and can hold a reference to any TCustomEdit descendant.

type
  // Friend class to get access to protected methods
  THackCustomEdit = class(TCustomEdit);
class function TtiCustomEditMediatorView.ComponentClass: TClass;
begin
  Result := TCustomEdit;
end;
function TtiCustomEditMediatorView.GetCurrentControlColor: TColor;
begin
  if THackCustomEdit(View).ReadOnly then
    result := ColorToRGB(ControlReadOnlyColor)
  else
    result := inherited GetCurrentControlColor;
end;

There is NO other way to implement this, except to create a mediator for each and every TCustomEdit descendants. Implementing the alternative would have been lots and lots of code duplication, and new mediators for each new TCustomEdit descendant that arrives in the market. Using the "friend class hack" means MUCH less code, and things still work perfectly.

Regards, ? - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net

Reply by email

Unknown author
to Graeme Geldenhuys
Graeme Geldenhuys wrote 14 lines
On 19 April 2012 00:04, Alberto Narduzzi wrote: > > An interface to a class (or its concept, anyway) is there for a reason. And > you should adhere to. I may also say that if you need to access a protected, > or private for what is worth, member of a (library...) class, then you > should revise your code... because either you're using the wrong class, or > your problem can be solved in a different, possibly more elegant, and surely > more OO compliant way ;-) I totally disagree... :) type // Friend class to get access to protected methods THackCustomEdit = class(TCustomEdit);

The Hack says it all. Here you are working outside regular OOP rules.

The correct way would have been an implementation for each descendent of TCustomEdit.

You just took a shortcut. Nothing wrong with that by itself, but basing an argument about general OOP rules on a shortcut implementation is incorrect reasoning.

In a system with stricter rules, you would have had to solve it differently.

Michael.

Reply by email

Hans-Peter Diettrich
to Reimar Grabowski: On Wed, 18 Apr 2012 09:50:16 +0200 Not being able to access a member that you should not…
Show quoted text 8 lines
On Wed, 18 Apr 2012 09:50:16 +0200 Hans-Peter Diettrich wrote: > It should be mentioned that class helpers do not solve all > problems, Not being able to access a member that you should not access is not a problem. It's what visibility in OOP is ment to do. It is by design that you cannot access that member.

Even designers are humans, and they can err.

The VCL and LCL contains many classes with a too restrictive design :-(

DoDi

Reply by email

Graeme Geldenhuys
to Unknown author: The Hack says it all. Here you are working outside regular OOP rules. The correct way…
The Hack says it all.

The "Hack" part is just a name. I could very well have named that TCustomEditFriend.

Here you are working outside regular OOP rules.

Saying that, then how does the new Delphi/FPC "friend class" language feature fit into that statement of yours. Or how does Delphi's new RTTI which can access private and protected class data fit into this. Delphi is not the only language with that "RTTI" feature. C# in .NET and Java etc all have support for what is now know as Reflection. They have full access to everything defined in a class.

If there wasn't a need for sometimes "breaking the class visibility rules", the RTTI or Reflection would never have been implemented in the various programming languages.

The correct way would have been an implementation for each descendent of TCustomEdit.

Have you got any idea how many TCustomEdit descendants there are? Delphi supplied ones and 3rd party ones? All I can say is, there are a lot, and having to modify the tiOPF framework for every new 3rd party or delphi component is nuts. There goes the rule of OOP code reuse. And my example applies to all standard components and their descendants, not just TCustomEdit.

As you might know, I am a big proponent of OOP, but I would be lying to myself if I said OOP is perfect. There are valid cases where OOP rules need to be bent ever so slightly. Reflection, the new Delphi RTTI and the concept of "friend classes" are all proof of this.

Also, as I mentioned, this is one example. There are many other cases too. Maybe they will make even better arguments, but we would never know.

Regards, ? - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net

Reply by email

Martin Schreiber
to Unknown author: The Hack says it all. Here you are working outside regular OOP rules. The correct way…
On Thu, 19 Apr 2012, Graeme Geldenhuys wrote: > On 19 April 2012 00:04, Alberto Narduzzi
Show quoted text 24 lines
>> An interface to a class (or its concept, anyway) is there for a reason. >> And you should adhere to. I may also say that if you need to access a >> protected, or private for what is worth, member of a (library...) class, >> then you should revise your code... because either you're using the >> wrong class, or your problem can be solved in a different, possibly more >> elegant, and surely more OO compliant way ;-) > > I totally disagree... :) > type > // Friend class to get access to protected methods > THackCustomEdit = class(TCustomEdit); The Hack says it all. Here you are working outside regular OOP rules. The correct way would have been an implementation for each descendent of TCustomEdit. You just took a shortcut. Nothing wrong with that by itself, but basing an argument about general OOP rules on a shortcut implementation is incorrect reasoning. In a system with stricter rules, you would have had to solve it differently.

May I repeat the idea of "friend units"?
While designing a complex framework like Lazarus, fpGUI or MSEgui there constantly is the need to access low level functions from other classes. Now one has the options to write the whole framework in a single unit (ex. db.pas), to make most of the class elements public or to use local alias type definitions.
For me public class elements should be "user suitable" and safe. I don't think it is a good idea to place elements the user of the framework normally should not touch to public.
And I don't think that in a project of that dimension it is possible to maintain a completely clean OOP structure with an acceptable effort. The introduction of "friend units" would be a simple solution. In "friend units" the protected class members would be visible. So if you build a framework and you know what you do, list the base units of the framework as friends of higher level units of the same framework. Users of the framework don't need friend units and can use the public class elements in a save manner.

Martin

Reply by email

Unknown author
to Martin Schreiber
Martin Schreiber wrote 29 lines
On Thursday 19 April 2012 09:52:30 wrote: > On Thu, 19 Apr 2012, Graeme Geldenhuys wrote: >> On 19 April 2012 00:04, Alberto Narduzzi wrote: >>> An interface to a class (or its concept, anyway) is there for a reason. >>> And you should adhere to. I may also say that if you need to access a >>> protected, or private for what is worth, member of a (library...) class, >>> then you should revise your code... because either you're using the >>> wrong class, or your problem can be solved in a different, possibly more >>> elegant, and surely more OO compliant way ;-) >> >> I totally disagree... :) >> type >> // Friend class to get access to protected methods >> THackCustomEdit = class(TCustomEdit); > > The Hack says it all. Here you are working outside regular OOP rules. > > The correct way would have been an implementation for each descendent of > TCustomEdit. > > You just took a shortcut. Nothing wrong with that by itself, but basing an > argument about general OOP rules on a shortcut implementation is incorrect > reasoning. > > In a system with stricter rules, you would have had to solve it > differently. > May I repeat the idea of "friend units"?

You can already use class helpers.

Michael.

Reply by email

Marc Weustink
to Mattias Gaertner: On Tue, 17 Apr 2012 17:06:47 +0200 I don't see many worms in the can, so I made it public.
Mattias Gaertner wrote 47 lines
On Tue, 17 Apr 2012 17:06:47 +0200 Marc Weustink wrote: > Mattias Gaertner wrote: >> On Tue, 17 Apr 2012 12:12:21 +0200 >> Marc Weustink wrote: >> >>> Mattias Gaertner wrote: >>>> >>>> Reimar Grabowski hat am 16. April 2012 um 16:42 >>>> geschrieben: >>>> >>>> > Hi, >>>> > >>>> > I load a TLazIntfImage from disk, manipulate it and show it on my >>>> form in a TImage. Center, Proportional and Stretch properties of the >>>> TImage are set to true. >>>> > Now I want to draw some overlays onto it (drawing to the canvas in >>>> the OnPaint event). Therefor I obviously need the origin and dimensions >>>> of the loaded image in 'canvas space', but I did not find a way to >>>> retrieve this information. Do I have to calculate them myself or can the >>>> TImage give me the needed information? >>>> >>>> The image is painted to the area defined by the protected method DestRect. >>>> >>>> Maybe this can be made public. >>>> >>>> At the moment you have to descend your own TImage class to access it or >>>> write a hack class (e.g. via a class helper). >>> >>> TImage is not meant to draw to. If you want to custom paint your image, >>> use a TPaintbox >>> >>> A TImage is for display only. >> >> TImage has a published OnPaint event. Painting some overlays makes >> sense. And for this you need to know where the image is on the canvas. > > Who has added this ? Delphi has no OnPaint event. > > Yes, if we have an event, we should provide a way to paint. > But to me this is a can of worms, since stretching is handled internally > while the user event draws on the canvas, independent of the stretching. > If you ask me, I wouldn't have added it. I don't see many worms in the can, so I made it public. It's a useful feature that costs us not much.

On scaled images there is no way to tell which source pixel corresponds to a destination pixel.

Marc

Reply by email

Mattias Gaertner
to Marc Weustink
Show quoted text 16 lines
[...] >>> TImage has a published OnPaint event. Painting some overlays makes >>> sense. And for this you need to know where the image is on the canvas. >> >> Who has added this ? Delphi has no OnPaint event. >> >> Yes, if we have an event, we should provide a way to paint. >> But to me this is a can of worms, since stretching is handled internally >> while the user event draws on the canvas, independent of the stretching. >> If you ask me, I wouldn't have added it. > > I don't see many worms in the can, so I made it public. > It's a useful feature that costs us not much. On scaled images there is no way to tell which source pixel corresponds to a destination pixel.

So?

Mattias

Reply by email

Hans-Peter Diettrich
to Reimar Grabowski: On Wed, 18 Apr 2012 09:58:56 +0200 What does depending on the compiler mean? The compiler…
Show quoted text 7 lines
On Wed, 18 Apr 2012 09:58:56 +0200 Hans-Peter Diettrich wrote: > But often (depending on the compiler) the mere declaration > of an empty derived class allows to access protected members of the base > class. What does depending on the compiler mean? The compiler is obviously FPC.

The implementation can vary in Delphi versions, whereupon it's unclear what FPC implements in which version.

DoDi

Reply by email

Hans-Peter Diettrich
to Alberto Narduzzi: I guess this must be considered a bug. You can indeed access protected members of parent…
Show quoted text 10 lines
>> But often (depending on the compiler) the mere declaration >> of an empty derived class allows to access protected members of the base >> class. > What does depending on the compiler mean? The compiler is obviously FPC. > If it is really true that you can access protected members in that way > (like Svens message suggests) I consider this a bug. > Like Mattias said, protected is of no real use in that case and we can > just declare all non private members public. I guess this must be considered a bug.

It's not a bug, it's a feature (of Delphi ;-)

Show quoted text 6 lines
You can indeed access protected members of parent classes, but only from within the (your derived) class members themselves, not from the outer world. At least this I believe to e the meaning of having protected (me and my friends only), together with public (everybody) and private (me and only me).

Apropos "friends": the C++ visibility model is different from the Delphi model. In Delphi classes the visibility applies to *unit level*, i.e. *all* code in the same unit can access protected (and private?) members of the declared classes. This behaviour can be modified with the later added "strict private/protected" visibility, which applies to *class* level. Similar to C++, where "friend" classes can be added to a class declaration, a dummy declaration of a derived class makes all protected members of the base class visible in the same *unit*. Similar but not identical.

DoDi

Reply by email

Reimar Grabowski
to Hans-Peter Diettrich: Even designers are humans, and they can err. The VCL and LCL contains many classes with a…
The VCL and LCL contains many classes with a too restrictive design :-(

First I don't care if the VCL is too restrictive or what Delphi in general does. This is not a Delphi list. Second if you think that some LCL classes are too restrictive you can always change them (as you have the source) and provide a patch. If your argument is reasonable the Lazarus developers will surely apply your patch.

R.

Reply by email

Hans-Peter Diettrich
to Unknown author: The Hack says it all. Here you are working outside regular OOP rules. The correct way…
Show quoted text 5 lines
> type > // Friend class to get access to protected methods > THackCustomEdit = class(TCustomEdit); The Hack says it all. Here you are working outside regular OOP rules.

It's only working around OPL deficiencies, for which e.g. C++ offers different OOP means (friend...). Without the introduction of means, upon which above hack is based, the OPL (and VCL/LCL) would be much less useful.

DoDi

Reply by email

Reimar Grabowski
to Graeme Geldenhuys: The "Hack" part is just a name. I could very well have named that TCustomEditFriend.…
C# in .NET and Java etc all have support for what is now know as Reflection. They have full access to everything defined in a class.

At least in Java reflection does not grant you access to everything defined in a class in the general case. And its primary purpose is not to access members that are protected/private. You are correct that it can be done using reflection but you must be very explicit about it. In general reflection does not change standard Java visibility rules. Normally you use getField/getMethod which only grant you access to visible members. If you need more information you call getDeclaredField/getDeclaredMethod which gives you information even about protected/private members but does not enable you to access them. Only when you call setAccessible(true) on this member you disable Javas access checks for this member. So in general reflection does not violate OOP principles and it is used like this most of the time for example to dynamicaly instantiate classes by name. It is possible to access members that should be hidden but you must enable access to every protected/private member you want to access explicitly. You are still working outside of OOP principles if you do this and are forced to jump through some hoops before Java grants you access to what should be hidden.

R.

Reply by email

Graeme Geldenhuys
to Reimar Grabowski: On Thu, 19 Apr 2012 10:08:23 +0200 First I don't care if the VCL is too restrictive or…
Reimar Grabowski wrote
First I don't care if the VCL is too restrictive or what Delphi in general does. This is not a Delphi list. Second if you think that some LCL classes are too restrictive you can always change them (as you have the source) and provide a patch. If your argument is reasonable the Lazarus developers will surely apply your patch.

Many years ago, I thought the same way as you do. Unfortunately it was made clear to me that the LCL's goal is to be a Delphi VCL clone! Delphi dictates what LCL is allowed to do - simple as that. Patches to the contrary will not be accepted. Sad but true - hence I moved on to greener pastures.

Regards, ? - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net

Reply by email

Graeme Geldenhuys
to Reimar Grabowski: On Thu, 19 Apr 2012 10:21:12 +0200 At least in Java reflection does not grant you access…
Reimar Grabowski wrote
It is possible to access members that should be hidden but you must enable access to every protected/private member you want to access explicitly.

And this is exactly what I have done in Object Pascal. I see to problem with what I have done. But yes, everybody is entitled to their own opinion.

Regards, ? - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net

Reply by email

Sven Barth
to Graeme Geldenhuys: Many years ago, I thought the same way as you do. Unfortunately it was made clear to me…
Graeme Geldenhuys wrote 10 lines
On 19 April 2012 14:06, Reimar Grabowski wrote: > First I don't care if the VCL is too restrictive or what Delphi in general does. This is not a Delphi list. > Second if you think that some LCL classes are too restrictive you can always change them (as you have the source) and provide a patch. If your argument is reasonable the Lazarus developers will surely apply your patch. Many years ago, I thought the same way as you do. Unfortunately it was made clear to me that the LCL's goal is to be a Delphi VCL clone! Delphi dictates what LCL is allowed to do - simple as that. Patches to the contrary will not be accepted. Sad but true - hence I moved on to greener pastures.

The DestRect property/function of TImage that is the reason of this thread was moved to public and this property doesn't even exist in Delphi (like the OnPaint event for TImage)...

Regards,
Sven

Reply by email

Graeme Geldenhuys
to Sven Barth
Sven Barth wrote
The DestRect property/function of TImage that is the reason of this thread was moved to public and this property doesn't even exist in Delphi (like the OnPaint event for TImage)...

And as you should very well know, the core developers of the Lazarus project do not live by the same rules as everybody else. They don't need to justify any of the changes they make.

And if you read this thread in detail, other developers raised concern over Mattias's change - yet he still sees nothing wrong with it, so his change will probably stay.

Different rules for different people. But this is not my project, so my opinion is worth nothing here. I'll give this thread a rest now.

Regards, ? - Graeme -

Reply by email

Reimar Grabowski
to Graeme Geldenhuys: And this is exactly what I have done in Object Pascal. I see to problem with what I have…
On 19 April 2012 14:39, Reimar Grabowski wrote: > It is possible to access members that should be hidden but you must enable access to every protected/private member you want to access explicitly. And this is exactly what I have done in Object Pascal.

Not quite. You have granted access to ALL protected members and not explicitly for single members. That's quite a difference. Either you fail to understand the difference between your 'hack' and what Java reflection does or you ignore it. Either way your way and the Java way are not 'exactly' the same. Nothing wrong with your code if it does what you need but your above statement is just not correct.

R.

Reply by email

Hans-Peter Diettrich
to Martin Schreiber: May I repeat the idea of "friend units"? While designing a complex framework like…
Show quoted text 6 lines
May I repeat the idea of "friend units"? While designing a complex framework like Lazarus, fpGUI or MSEgui there constantly is the need to access low level functions from other classes. Now one has the options to write the whole framework in a single unit (ex. db.pas), to make most of the class elements public or to use local alias type definitions.

This also requires to modify the existing classes. If every user can do that, encapsulation *really* has gone away :-(

The introduction of "friend units" would be a simple solution. In "friend units" the protected class members would be visible. So if you build a framework and you know what you do, list the base units of the framework as friends of higher level units of the same framework.

See "framework" as an special case of what a user wants to achieve. Where will you draw a boundary between *acceptable* and *unwanted* breaking of visibility, by using friends, class helpers or any other means?

DoDi

Reply by email

Hans-Peter Diettrich
to Reimar Grabowski: On Thu, 19 Apr 2012 10:08:23 +0200 First I don't care if the VCL is too restrictive or…
Show quoted text 10 lines
On Thu, 19 Apr 2012 10:08:23 +0200 Hans-Peter Diettrich wrote: > The VCL and LCL contains many classes with a too restrictive design > :-( First I don't care if the VCL is too restrictive or what Delphi in general does. This is not a Delphi list. Second if you think that some LCL classes are too restrictive you can always change them (as you have the source) and provide a patch. If your argument is reasonable the Lazarus developers will surely apply your patch.

When I wanted to implement an alternative DragManager, I found this almost impossible outside of the Controls unit, due to very many references to protected members of TControl and TWinControl. Do you honestly believe that all these members will ever be made public, only for the sake of another DragManager implementation?

Hint: look at the many patches I already supplied, and how many of them have been accepted :-(

DoDi

Reply by email

Mattias Gaertner
to Graeme Geldenhuys: And as you should very well know, the core developers of the Lazarus project do not live…
Show quoted text 14 lines
On 19 April 2012 19:29, Sven Barth wrote: > > The DestRect property/function of TImage that is the reason of this thread > was moved to public and this property doesn't even exist in Delphi (like the > OnPaint event for TImage)... And as you should very well know, the core developers of the Lazarus project do not live by the same rules as everybody else. They don't need to justify any of the changes they make. And if you read this thread in detail, other developers raised concern over Mattias's change - yet he still sees nothing wrong with it, so his change will probably stay.

I proposed a change and waited for comments.
Marc pointed out that Delphi does not have that feature and it might be a can of worms.
So I checked the can.
Custom painting on a TImage was always possible, for example by overriding Paint. The OnPaint is just making it easier. TImage.OnPaint exists since many years and many people use it, so it is useful and should be kept.
As discussed in the other thread, there is no big difference between public and protected.
I didn't found any discussions about TImage problems, except for TPicture and the two Canvas.
I didn't found a clue that DestRect will open a can of worms, so I made it public.

Same happened with many of my concerns. Raising a concern is not a veto.

Different rules for different people. But this is not my project, so my opinion is worth nothing here. I'll give this thread a rest now.

Mattias

Reply by email