[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