[Lazarus] Win32 HICON

JoshyFun joshyfun at gmail.com
Thu Dec 18 23:55:46 CET 2008


Martin Friebe schrieb:
> In some places below, I am unsure what you meant by "component":
> - the "view" (as in MVC)
> - the overall editor (as in SynEdit)

Now that you mention it, I'm also unsure. I meant the implementation of 
the component, without thinking of the embedding/outsorcing of the 
controller, or whether the control should include an default controller 
for a default (new text file) data source.


> Serializing the Model for storage purposes (such as saving to a file or 
> many files) for me comes after the decision how the model looks.

But we can decide in advance, which properties are to be stored at all, 
or will have to be saved and restored when the data source (file) is 
exchanged.


>>> We could differ between a public and a private model. (Probably not needed)
>>>     
>> Most probably not needed in this form, default is the public Model.
> Actually we may need. Example folding.
> Having the same model displayed in many Windows, the user may want to 
> fold different nodes in each Window.
> Or even use different highlighters, leading to different nodes being 
> available.

All that IMO is configurable, using according stacks. The component code 
uses a given helper object, without knowing whether it's private or shared.

> The design of this must leave the choice to the user, which information 
> is hold in the public, and which in the private model. The private Model 
> can store the same info as the public. The private model only stores 
> info the user whises to be different from the public model, all other 
> info will be forwarded.

Or the private Model is independent from outside data, and all shared 
properties have to be transferred into the View, by the (application 
wide) Controller. The public Model cannot know what a View has to do, 
when settings change, so that all changes must be signaled to the Views. 
In an stack, we need an back-channel for propagating such configuration 
changes to the other end.

This may be related to what I called "characteristic" data. The View 
contains an set of properties, for private use, and the Model contains 
another set of shared properties. When the shared properties change, a 
single change notification is sent to each View, with the changed 
property set, and every View has to check for changes and react as 
appropriate. That's easier to implement, and doesn't leave an View in an 
inconsistent state, when one of more related settings change.

I remember FontChanged and other methods in SynEdit, which may have to 
be embedded in a wider scope, e.g. guarded by BeginChange/EndChange 
calls. All intermediate changes are stored, but are recognized only on 
the final EndChange. When all acutal changes are collected in an set of 
ChangeFlags, the receiver can know which settings actually have changed, 
and which (other) ones may deserve an according readjustment.

I already planned to open another thread, on my design for such 
synchronizations, in detail for synchronizing scrolls and caret moves.


> However concerning the design of the other Classes (Controller/View), 
> the differentation between private or public model is not relevant. They 
> access one Model. This Model knows what to do.

IMO the Model has to know nothing, except that it holds data, and 
possibly how to load/store these data in a customized class (overridden 
methods).


>> View.Folding := Model.Folding; //object reference
>> if FoldingPerView then begin
>>    PrivateFolding := TFoldingState.Create(View.Folding);
>>    View.Folding := PrivateFolding;
>> end;
>>
>> [This is what you would consider as "pushing" another item on the stack, 
>> see below]
>>   
> My implementation probably looks slightly different. There will be no 
> dedicated properties (on the View) for dedicated features.
>   View.Model := TheModel;
> Then the View can internally distribute this. The Model can be a Private 
> Model, which acts as a wrapper to the public Model.

I should have used Self instead of View, to indicate that my code 
resides inside the View, in the View.Model property setter. Then we 
agree again, I assume?


[encoding]
> For SynEdit in Lazarus, currently UTF8. Ideally most of the code should 
> be agnostic to the encoding, and the remainder exchangeable.
> The design of the Classes involved most not be based on an encoding. 
> (except maybe the design of the model)

The data exchange between the classes IMO should use fixed data types. 
When TStrings are used, an agreement must exist whether the strings are 
Ansi or UTF-8 encoded. In the CharGrid I used WideString in the 
interface (assuming UCS-2), and left it to the Model to translate the 
string encoding, between file format, internal storage, and 
viewer/contoller interface.


>> Wordwrap should be implemented in the component, because it depends on
>> the width of the window. Folding should be reflected in a helper object, 
>> that allows to retrieve the visible lines. In the simplest case (source 
>> not foldable at all) that object does a 1:1 mapping of the unfolded lines.
>>   
> Which Component ? The View (as in MVC)?

The component implementing an View.


> In my design both Folding and WordWrap will be a Class on the "Views" 
> internal Stack.
> 
> If the MVC is correctly implement, then the only difference between 
> Folding or not Folding is:
> - An extension to the Model: This should be generic, by adding a class 
> to it without the need of changing the Container.
> - A Class added to the Views Stack. Again no other code in the View 
> should change.
> 
> "no other code should change" of course this is not possible, but there 
> are ways to get very close.

IMO it's possible, as outlined above. An agreement must exists about the 
base class, that represents foldable text. The interface of that class 
allows to retrieve lines (maybe as "raw" or "folded"), and to retrieve 
information about the blocks (tree) at all. Everything is implemented 
for shared use (by multiple Views) in the Model.FoldedText (.FoldData) 
object. When folding should be synchronized in all Views, every View 
accesses that object directly. When folding shall be different for an 
specific View, it stacks his private folding object on top of the 
Model's object, and the private object (class) implements the private 
collapsed/expanded state of the shared blocks. Since the private class 
inherits from the common class, it has the same interface, and no other 
code deserves any adaptation; except for the initialization of the 
stack, of course.

[...]
> Yet, while such a generic aproach is noce, for very common features it 
> may be better to add direct accessor ("FoldData := Model.FoldData;")
...and the FoldData setter method will do whatever is required :-)

In the destructor a FoldData := Nil; statement will destroy an 
eventually created private object, so that the destructor deserves no 
special code or knowledge about private helper objects.


> Have you taken into account the difference between the TextDrawer, and 
> the decorated TextDrawer? The above comment was not refering to the full 
> "View component". It was talking about the undecorated TextDrawer.

I do not distinguish between an un/decorated TextDrawer - one is the 
CustomDrawer class, the decorated one *is* the customized class. A 
separation into multiple classes IMO is restricted to very basic 
functionality, like a mere text painter. Just a gutter painter deserves 
information about the scrolled line numbers, so that a base class 
without access to such information (stored in or provided by the 
component/View class) looks useless to me.


> Yes ViewPort Management is part of the View. But not of the undecorated 
> textDrawer.

In my model your undecorated TextDrawer is reduced to a single (virtual) 
method, for painting a single row (of characters). An overridden method 
will prepare the actual text and attributes (line buffer), before 
calling the inherited method. The gutter painter is a self-contained 
object, that is already incorporated into the very basic component class 
(TBaseGrid), and deserves no further consideration in a derived class.



>> This depends on the old and new position in the text and canvas. As long 
>> as the new position is alreday visible, no redraw is required at all. 
>> Only the component has all information available, to determine what 
>> really has to be done.
>>   
> Yes, but I wasn't going into that much detail yet. In that case we have 
> to handle external invalidation too. The OS may tell us to redraw parts 
> of the Window

Right, that's why a component should tell the OS, which parts of the 
viewport deserve an refresh, and wait for the resulting paint message. 
The OS will collect such requests and send one or more paint messages, 
taking into account hidden parts of the window. A component should not 
try to figure out and manage all such details itself (reinventing the 
wheel ;-)


>>> The component should also concern the application of highlighting or 
>>> Markup information, provided it can retrieve it from the appropriate 
>>> classes. (This will allow multiply Components to display the same Model 
>>> , using different highlighters)
>>>     
>> It's a matter of invoking the methods of the right object in the right 
>> moment. The object references are initialized once, their methods are 
>> invoked when required.
>>   
> Yes. That is what I thought we talked about in the earlier mails in this 
> thread?

Until now I pointed out, how my CharGrid can take into account *all* 
possible extensions, by design. With the only restriction to character 
cells of a unique size. Even the case of double-width characters can be 
handled, somehow. I did so in order to prove the usability of my 
approach, so that the procedures (abstract, not subroutines) can be 
applied in an redesign of the SynEdit component.


> For me this still is a having the right Class(es) on the Stack (the 
> stack inside the View). Those Classes will have access of the Model (or 
> parts of the model; as they will have been initialize accordingly). Each 
> Class performs one step of the transformation.
> Any knowledge about those tranformations outside those classes should be 
> minimized (or ideal not exist at all)

With regards to encapsulation I see no special difference, between 
calling a (virtual) method of the same class, or of a helper class.

> This does allow the stack to contain any kind of transformation class. 
> Classes can be added without changing any other code. (or with a minimum 
> of those changes)

With regards to (data) consistency I see possible problems in an 
configurable stack, because it fixes the order of the transformations. 
This may make sense with predefined protocol levels, but not necessarily 
with tab expansion, syntax highlighting etc., as required in the 
preparation of the text for painting. In detail the (hypothetical) 
handling of double-width characters or proportional fonts affects the 
caret positioning, transformation of mouse coordinates, and more, so 
that an according helper object must be accessible directly, for 
specific purposes apart from painting.



> In the stack every class knows about it's next higher level (so it knows 
> it input). In that it is similar to a linked list or pipe.

This was my misunderstanding, with regards to push/pop operations in an 
traditional stack, where the pushed objects do know nothing about their 
neighbourhood.

> A linked list (or list in general), I would understand as something that 
> allows me access (from the outside or controller / controller only if 
> encapsulated) to elements in the middle. With a stack there should only 
> be access to the bottom element (maybe in some implementations to the 
> top element / I do not plan to access the top element)

And as mentioned above, a need may occur to access distinct elements, 
apart from their placement in an stack or chain for particular operation 
sequences.


>>> As you can see in the image, I divide the TextDrawer into 2 sets of classes.
>>>     
>> I see. It's a matter of taste whether the ScrollBars are separate 
>> elements, or are part of a ScrollableWindow - in either case they 
>> determine the logical origin of the canvas. IMO also the TextDrawer 
>> *implements* the Logical View, eventually using helper objects.
>>   
> Ack, it uses the Stack, with all the classes on it.
> 
> The canvas is passed in at setup time.

I see a lot of stacks, not only one :-(

In the case of scrollbars, there must be reserved space in the window, 
their ranges and positions have to be adjusted dynamically, so that I 
cannot see how they would fit into any stack.

[...]

> The TextDrawer (undecorated) is the Facade (component) to the following 
> objects:
> - the stack (with all it's classes)
> - the painter
> 
> So did you mean: for the stack objects to provide the most appropriate 
> interface for the Painter?

I see multiple stacks, e.g. one for the text (painter), others for line 
numbers (folding...), bookmarks etc. (for gutter painter and UI). I'd 
prefer to view these as multiple pipes, connecting View and Model, as an 
extension to direct access to properties or helper objects of the Model.



>> Could we agree about an CustomTextDrawer (as a general base class) and 
>> an derived/customized LazSynEdit (as a readily usable component, 
>> presented in the component palette)?
>>   
> Yes, A SynEdit should be customizable by picking individual other 
> SynHelper components from the palette. (alternatively see below)

Interesting idea. But I wonder how a SynEdit component shall know how to 
make use of such a dropped object, in detail when such objects are of 
the same or of a new class, not yet known to the SynEdit.


>> The customization would consist primarily of the construction of the 
>> stacks (pipes between Model and View), and secondarily in the reflection 
>> of eventually modified interfaces of the stack objects, i.e. in the code 
>> to access these objects.
>>   
> It may be more convenient to provide a special User Interface for this. 
> Something like the anchor editor. In which you can select parts of your 
> synedit

You mix up the component configuration at design- and runtime?

DoDi




More information about the Lazarus mailing list