[Lazarus] Design patterns / SourceEdit and SyneEdit

Hans-Peter Diettrich DrDiettrich1 at aol.com
Thu Dec 18 22:07:35 CET 2008


Let me start an thread about the internal design of such components.

Martin Friebe schrieb:
[...]
>>> In terms of MVC this needs more clarification.
>>>     
>> You mean, when MVC is applied to the internal structure of the 
>> TextDrawer, in contrast to the application viewpoint?
>>   
> No, the MVC has no idea about those internals. This line was only to 
> indicate that some of my considerations (including most of my 
> considerations in previous emails) where not made in the context of the 
> "View" only. (Meaning I was exploring the internals of a MVC View and 
> not the Model or Controller / The presence or need of I was taking for 
> granted at that time, leaving it out of discussion)

IMO it makes sense to apply MVC also to the internals of a visual component.

[...]
> In the current implementation, if something changes, this something 
> often knows about everyone else who needs to react to this change, and 
> makes direct calls to all of them. That's real bad.
> I' d like to revert that. Individual elements know, on who's changes 
> they need to react, and register a callback handler. If the other one 
> changes, it just makes all the callbacks. It has no need to know who 
> receives the callback.

I deny the usability of callbacks here, and present a different (proven) 
model, as I implemented in the CharGrid.

> Example:
> Interaction between Caret and Viewport.
> - If the caret moves, the viewpoer may have to move, so currently the 
> caret calls the viewport.
> Better: the Viewport just registers on the carets OnChange callback List.

Even better:

Scrolling, folding, or caret movements (hyperlink or bookmark jumps...) 
can affect both the caret position and the viewport origin. In order to 
synchronize both, a centralized dispatcher comes into mind.

Every event is translated into a primary action, by the controller. The 
action e.g. describes whether the viewport should follow the caret move, 
or the caret should follow the viewport move, or should stay where it is.

The dispatcher (or local controller) then determines all *consequential* 
changes, required to reflect the *intended* effect. It initializes an 
set of flags, indicating what has to be done, what already has been 
considered, and what remains to do, to reflect the new state (display 
refresh).

Everything starts with a BeginUpdate, preventing premature operations. 
When the caret shall be moved, or the viewport shall be scrolled, then 
according flags prevent a later re-adjustment of these values. Then all 
further checks and adjustments are made, like clipping the caret and 
viewport to the extent of the actual text, scrolling the caret into 
view, or moving the caret into the new viewport, and how to adjust the 
scrollbars, when a block has been collapsed or expanded. Also 
dependencies between vertical and horizontal moves are checked. The 
sequence of the checks and adjustments can be fixed, at least in case of 
the CharGrid, so that it's easy to debug and re-order the various steps, 
if required.

A recursion into already invoked methods does no harm, so that a 
vertical scroll check can invoke an horizontal scroll check and vice 
vera, because already performed checks simply do nothing. Each procedure 
only must know about related checks, to be triggered, but must not 
assume anything about which other checks may have to be bypassed under 
certain circumstances. This makes a significant difference vs. setting 
individual properties one by one, where the setter methods might invoke 
each other, possibly causing infinite loops, or producing unintended 
inappropriate results.


When the new state has been determined, the last EndUpdate triggers the 
required refresh of all components (caption, scrollbars, status bar, 
text and gutter areas etc.), based on the flags in the action/state flag 
set. In that execution state some internal properties can be re-adjusted 
or updated, e.g. the physical (visible) caret position can be forced to 
the end of a short line, while the logical caret position stays in the 
the same column as before a vertical move. The affected display area is 
determined and invalidated, with chances for optimizations, as far as 
the OS and widget set allow. One such optimization (Win32) were 
scrolling the canvas immediately, and repainting only the uncovered 
client area.


Such a strict separation between initial commands, state changes, and 
final response, centralized in kind of an local Controller, will result 
in an almost bullet proof implementation. All modifications to the 
remaining code in the component class are allowed to use only the 
predefined dedicated methods.

DoDi




More information about the Lazarus mailing list