Browse files

second tutorial snapshot save

  • Loading branch information...
1 parent ceb6a35 commit ee8c7788141809ce0fe57cfe2d0f8c9a3ae047fb megaannum committed Aug 23, 2012
@@ -448,7 +448,7 @@ inheritance framework ({Self.vim} is the {Forms} library's sole dependency).
At the base of the Glyph inheritance tree is the Glyph (forms#Glyph) which
has the Self Object Prototype as its prototype. The Glyph prototype has
-method that define the behavior of all derived Glyph types as well as
+methods that define the behavior of all derived Glyph types as well as
inheriting the Self Object's methods. When the term {Glyph} is used, much
of the time it refers to an object that is based upon the Glyph Prototype.
@@ -0,0 +1,190 @@
+# Creating a new Glyph
+## Minimum Methods
+Since there already exists the 'null' Glyph (g:forms#NullGlyph)
+that has no size and draws nothing, any Glyph a developer might
+define needs to have both a size and renders something.
+* requestedSize()
+ This method returns the size, List containing width and height, that
+the Glyph would like to occupy when drawn.
+* draw(allocation)
+ Draw the Glyph at the location and size given by the Dictionary 'allocation'.
+The allocation contains the key/values for 'line', 'column', 'width',
+and 'height'. A 'draw()' method generally follows this template:
+ function SomeGlyph.draw(allocation) dict
+ let self.__allocation = a:allocation
+ if self.__status != g:IS_INVISIBLE
+ " Code to draw Glyph
+ endif
+ " For interactive Glyphs
+ if self.__status == g:IS_DISABLED
+ " Code to 'color' the Glyph as "disabled"
+ " A "disabled" is one that can not accept focus
+ call AugmentGlyphHilight(self, "DisableHi", a)
+ endif
+ endfunction
+It is *critically* important when rendering a Glyph, that the Forms library
+can and does use multi-byte characters, UTF-8; that some character
+drawing commands will screw things up. It is best to rely on the
+existing Character and String drawing functions:
+ forms#SetCharAt(chr, line, column)
+ forms#SetHCharsAt(chr, nos, line, column)
+ forms#SetVCharsAt(chr, nos, line, column)
+ forms#SetStringAt(str, line, column)
+ forms#SubString(str, start, ...)
+See the bottom of the forms.vim code for usage.
+## Has Attributes
+If the new Glyph has any additional attributes, then it is also important
+to define the methods:
+* init(attrs)
+ While the Self Prototype 'init()' method will match attributes with their
+intended values in the attrs Dictionary, a Glyph might wish to define its
+own 'init()', which, of course, would call its Prototype's 'init()',
+in order to check the values of the attributes have been set to and
+make sure that all attributes that need to be set are set.
+ function SomeGlyph.init(attrs) dict
+ " call Prototype init method
+ call call(self.__prototype.init, [a:attrs], self)
+ " check attribute values, throw exception if bad
+ ...
+ return self
+ endfunction
+Very important, remember that the 'init()' method returns 'self'.
+* reinit(attrs)
+ A Glyph's 'reinit()' method is called if one wishes to change one or more
+attribute values. This is a safe but expensive way of changing a Glyph's
+attributes. This approach should be used is some fundamental aspect of a
+Glyph, some attribute that one expects to be constant, needs to be changed.
+Consider the Label's 'reinit()' method which changes the Label's text:
+ function! FORMS_LABEL_reinit(attrs) dict
+ let oldText = self.__text
+ let self.__text = ''
+ call call(g:forms#Leaf.reinit, [a:attrs], self)
+ if oldText != self.__text
+ call forms#PrependUniqueInput({'type': 'ReSize'})
+ else
+ call forms#ViewerRedrawListAdd(self)
+ endif
+ endfunction
+Generally, the text associated with a Label is expected not to change.
+In the above, the old value of the text is saved and then the Label's
+'init()' method is called. Upon its return, there are two possiblities:
+- The size (number of characters) in the new text is different from
+the size of the old text. In this case, the size of the Label has changed.
+This requires that the whole Form be re-size, an expensive operation.
+This is triggered by adding a 'ReSize' Event to the Input Queue. The
+'ReSize' Event is *not* handled by the Viewer's event loop but rather
+the Form's event loop. The Form must restore the existing text, figure
+out how big the Form with the new Label text must be, save the existing
+text, and then re-enter the Viewer event loop redrawing the whole Form in
+the process.
+- The second possibility is that the size of the old and new text is the
+same, so all that is required is for the Label to be redraw. This happens
+by adding the Label to the Viewer's Redraw List. After the Viewer
+event loop handles every Event, it checks its Redraw List to see if any
+Glyph or Glyphs need to be redrawn and, if so, those Glyphs and no other
+Glyphs are redrawn.
+## Interactive
+To go further, the developer must decide if the new Glyph is to be interactive
+or not and whether it will have children or not.
+If the new Glyph is interactive, it can get and lose (a Viewer's Event
+handling) focus; it has a 'hotspot' and may display a 'flash' if
+cursor movement within it exceed its boundaries; it probably will
+have user entered data which can be added to the 'results' Dictionary
+when the Form is exiting due to a 'Submit' Event; and it handles both
+characters and Events. So, an interactive Glyph needs to define the methods:
+* canFocus()
+ A Glyph can be enabled, disabled or invisible. If the Glyph is enabled, then
+and only then can it have focus, so the 'canFocus()' method should look like:
+ function SomeGlyph.canFocus() dict
+ return (self.__status == g:IS_ENABLED)
+ endfunction
+* gainFocus()
+ Sometimes a Glyph needs to take some action when it first gains focus.
+This method is used to tell the Glyph it has gained focus.
+* loseFocus()
+ Notify the Glyph that it has lost focus. This needs to be defined only if
+the Glyph will take some action upon losing focus.
+* hotspot()
+ Draw the hotspot associated with the Glyph.
+* flash()
+ This method is called if there has been some input error or cursor motion
+* addResults(resutls):
+ An interactive Glyph generally has user entered data. This method is called
+while the Form is handling an 'Submit' Event to enter the Glyph's data into
+the 'results' Dictionary;
+ function! SomeGlyph(results) dict
+ let tag = self.getTag()
+ let a:results[tag] = self.__some_data
+ endfunction
+If the Glyph has multiple chunks of data, the value entered into the
+'results' should be a ordered List of those chunks.
+* handleChar(nr)
+ Handles the Number returned from 'getchar()' when Glyph has focus.
+* handleEvent(event)
+ Handles the Event returned from Input Queue when Glyph has focus.
+In many cases, the result of handling a Character or Event is for the
+Glyph to add itself to the Viewers Redraw List. On some occasions, the
+Glyph will call its 'flash()' method when the entered data is invalid
+or results in a bad cursor (bad hotspot) position.
+In addition, the developer might wish to add context specific usage information
+to the Glyph which allows user to quick see how to use the Glyph, thus
+the 'usage()' method should be defined:
+* usage()
+ A Generic description of how a user interacts with the Glyph possible
+including what keyboard and mouse events are handled.
+## Non=Interactive
+When a Glyph is non-interactive, none of the above methods needs to be
+## Number of Children
+Any new Glyph must have as a Prototype ancestor either: Leaf, Mono, Poly
+or Grid; one should *never* derive directly from the Glyph Prototype.
+When deriving from Mono, Poly or Grid, many methods must be structured
+so that they call the Glyph's Prototype's corresponding method (unless
+one wishes to duplicate or replace all of the code).
@@ -0,0 +1,65 @@
+# Events
+There are two types of input events. The first type is simply the
+Character or Number returned by calling Vim's {getchar()} function.
+These are called "character" events. A {Viewer} examine all such
+character events and map some of them into the second type of event
+supported by {Forms}, a Dictionary object or object event that must
+have a 'type' key whose value is one of a set of allowed names. As an
+example, a <LeftMouse> Number returned by {getchar()} is converted
+into a Dictionary event object of type 'NewFocus' where the Dictionary
+also has entries for the line and column (v:mouse_line and
+v:mouse_column). Some object events are generated by the {Forms}
+runtime system. An example of such an object event is a ReSize event
+which is generated when a Glyph actually changes its size (normally, a
+very rare occurrence). More common runtime generated event objects are
+the Cancel and Submit events which are, generally, generated by a
+"close" and an "accept" button.
+A user interacts with a form by sending events to the form. Such
+events are generated by the keyboard and mouse. Events are
+read by the {Viewer} with current focus, mapped by that {Viewer}
+and then forwarded to the Viewer's child Glyph that has focus.
+If a {Viewer} has no child Glyphs that accept focus, then all events
+are ignored (except the <Esc> character which will pop the user out
+of the current Viewer/Form).
+If a Glyph consumes an input event, it might require redrawing (such
+as adding a character to an editor). In this case, the Glyph registers
+itself with a viewer-redraw-list and when control is returned to the
+{Viewer}, the Glyph is redrawn.
+Sometimes a Glyph will consume an event and an action will be
+triggered. For example, a left mouse click or keyboard <CR> entry
+on a button will cause the action associated with that button to
+## Limitations
+### New Types of Events
+A developer can create a new Event type, but the Glyphs that handle Events
+will simply ignore it.
+Currently, any Glyph that handles Events does so in one of its methods;
+there is no notion of a pluggable, independent Event Handler.
+### Representation
+I've thought about but never reached a design that I was happy with
+concerning the scripting of a Form interaction. That is, add Events
+to the Input Queue and then call a Form's 'run()' method.
+The issue is that of representing the Number and String that are
+returned by 'getchar()' and 'nr2char()' methods. And, in addition,
+some events have secondary data such as Mouse Events.
+A companion problem is how to log and capture a user's 'getchar()' Character
+stream in some format that would allow for editing the resultant
+captured data and replay.
+In both cases, I do not see a solution short of enumerating all possible
+Character/Event types.
+I might add, a good Character/Event capture/edit/replay capability would
+allow for creating a suit of non-visual unit tests for the Forms library.
Oops, something went wrong.

0 comments on commit ee8c778

Please sign in to comment.