The C64 OS Programmer's Guide is being written
This guide is being written and released and few chapters at a time. If a chapter seems to be empty, or if you click a chapter in the table of contents but it loads up Chapter 1, that's mostly likely because the chapter you've clicked doesn't exist yet.
Discussion of development topics are on-going about what to put in this guide. The discusssions are happening in the C64 OS Community Support Discord server, available to licensed C64 OS users.
C64 OS PROGRAMMER'S GUIDE
Class Reference: TKView:TKObj
Overview
TKView
is a fundamental class. It inherits from
TKObj, and all other classes that are provided with
C64 OS inherit from TKView
.
TKView
provides several groups of properties and methods allowing
TKView
itself and all of its subclasses to behave as nodes in a tree which defines
the structure of a user interface. The nodes are able to participate in handling and propagating
mouse and keyboard events. They also know how to resize and position themselves based on a set
of anchors and offsets. TKView
and all of its descendents have the ability to
draw themselves, and to perform mouse event hit testing.
TKView
may be instantiated directly when the user interface requires a generic
container for one or more subviews. For instance, if you want to construct a user interface that
consists of a scrollable list with a button below the list, a TKView
can be
instantiated as the container for both the TKScroll view and
the TKButton control.
TKScroll and TKButton
both inherit from TKView
and their offset and anchor properties can be set to size
and position them within their parent TKView
container.
When a TKView
is resized, it propagates the resizing to its children and computes
their new size and position. TKView
can also be used as an intermediate container for just a single
child view to create a border around that child which would ordinarily sit flush to the edges
of its parent. By setting the bcolor property of that container TKView
that border can have
a customizable color.
Subclassing notes
All other classes included with C64 OS inherit either directly or indirectly from TKView
.
TKView
implements very complex logic in its resize, update and hittest methods. Attempting
to reimplement any of these methods is not recommended. In order to retain their complex
behavior any TKView
subclass should call the superclass implementation of these methods.
Class Definition
//os/tk/h/:tkview.h
Init (init_)
During initialization, a TKView
's anchor properties are set to Top, Bottom, Left and Right,
and to propgate resizing events to its children. Its scroll offsets are zeroed. Its flags
are to mark it as: dirty, accepts first mouse, in bounds, visible but transparent, and requiring
an initial resize. Its background color is set to dynamically take the background color from
the system's color theme.
Responder Methods
Method | Description | Offset |
---|---|---|
setfirst_ | Set First Responder | 10 |
Method | Description | Offset |
dokeyeqv_ | Do Key Equivalent | 13 |
keypress_ | Key Press | 16 |
Method | Description | Offset |
musdown_ | Left Mouse Down | 19 |
musmovd_ | Mouse Moved | 22 |
musup_ | Left Mouse Up | 25 |
musclik_ | Left Mouse Click | 28 |
musdclik_ | Left Mouse Double Click | 31 |
rmusdwn_ | Right Mouse Down | 34 |
musalt_ | Mouse Alternate Event | 37 |
The responder methods are a group of methods that are responsible for event handling. The
TKView
implementation of the keyboard and mouse event methods do not directly affect the
TKView
object, but are responsible for propagating the event up the view hierarchy. Any
subclass of TKView
may reimplement an event handling method in order to perform some
custom behavior. Any method not specifically handled by the subclass should call the
superclass method in order to retain the essential event propagation behavior.
Set First (setfirst_)
Input | Description |
---|---|
C → Set | Set as first responder |
C → Clear | Resign as first responder |
Output | Description |
- | None |
Keyboard event routing begins with a view that is called the first responder. There can be only one first responder at a time. If there is no explicit first responder, the root view is the default first responder.
There is a bit flag in the dflags
property, Accepts First Key (df_afkey
). If this flag is
set when a mouse click event is handled, the first responder status is shifted to this object.
This is accomplished by calling setfirst_
with the carry set. Set First, internally, looks up
the current first responder object and calls setfirst_
with the carry clear. This causes the
current first responder to resign its status as first responder, just prior to this object
taking on the first responder status.
TKView
's accepts first key flag is unset by default. Subclasses of TKView
which handle key
events should enable this flag in their init_
method.
The first responder status can be programmatically shifted to any object (whose accepts first
key flag is set) by explicitly calling its setfirst_
method with the carry set. It is also
sometimes desired to programmatically have the current first responder lose its first
responder status without explicitly shifting the focus to some other object. This can be done
by calling the first responder's setfirst_
method with the carry clear.
The first responder object is pointed to by the Toolkit Environment's First Key View (te_fkeyv
)
property.
If a subclass of TKView
accepts first key and it undergoes a change of appearance as a result
of accepting first key status, it should reimplement the setfirst_
method. In its own
implementation it should set its dirty flag and then call the superclass setfirst_
method.
TKInput is an example of this behavior. When clicked it not only takes first key status but
its background color changes to distinguish it as the field in focus and it displays an input
cursor. When a TKInput loses first key status, it marks itself dirty again, so that it can be
redrawn without the input cursor and with the background color of an unfocused field.
Do Key Equivalent (dokeyeqv_)
Input | Description |
---|---|
- | None |
Output | Description |
- | None |
Do Key Equivalent is called when a low-level key command event is queued and being routed into the Toolkit environment to be processed.
TKView
's default behavior is to propagate the event up through the responder chain. The
responder chain is not identical to the node tree, although there is a relationship between
them.
When a key command event is to be propagated, it is sent to the object pointed to by this
object's next responder (nextresp
) property. If the next responder property is null, then
it is sent to this object's parent
node instead. Propagation continues until an object
handles the event and then does not propagate it any further up the responder chain, or
when the event reaches the root view.
Subclasses of TKView
which provide support for key command events should implement this
method. Any key command event not supported should result in calling the superclass dokeyeqv_
method.
Key Press (keypress_)
Input | Description |
---|---|
- | None |
Output | Description |
- | None |
Key Press is called when a low-level printable key event is queued and being routed into the Toolkit environment to be processed.
TKView
's default behavior is to propagate the event through up the responder chain. The
responder chain is not identical to the node tree, although there is a relationship between
them. The responder chain for printable key events is slightly more complicated than it is
for key command events.
When a printable key event is to be propagated, it is sent to the object pointed to by this
object's next responder (nextresp
) property. If the next responder property is null, then
it is sent to this object's parent
node instead. Propagation continues until an object
handles the event and then does not propagate it any further up the responder chain. If the
event reaches the root view, and the root view does not handle the event itself, TKView
's
default behavior is to check if the printable key event is the RETURN key.
If the RETURN key has propagated all the way to the root view, TKView
instigates a search
for the first TKCtrl object (or any of its subclasses) which is not disabled and has its
default flag set. If and when such a control object is found that control's Send Action
(sendact_
) method is called and the search terminates. The search is performed by
walk, and only walks the visible tree.
The check for whether a node is a subclass of TKCtrl is
performed by instanceof.
Subclasses of TKView
which provide support for printable events should implement this
method. Unsupported printable key events usually result in calling the superclass
keypress_
method, but may refrain from doing so under special circumstances.
All mouse events, except Left Mouse Click
Input | Description |
---|---|
- | None |
Output | Description |
- | None |
TKView
does not directly respond to mouse events. Its default behavior is to propagate the
event up the view hierarchy by calling the same mouse event method on this view's parent
view. Propagaion continues until an object handles the mouse event and does not propagate it
further, or the event reaches the root view.
Left Mouse Click (musclik_)
Input | Description |
---|---|
- | None |
Output | Description |
- | None |
As with all the other mouse events, TKView
does not directly respond to mouse click events.
Its default behavior is to propagate the click event up the view hierarchy until an object
handles the mouse click event and does not propagate it further, or the event reaches the
root view.
However, TKView
does have one custom behavior for the Left Mouse Click event specifically.
It checks to see if its Accepts First Key flag is set. If it's set, it calls setfirst_
with the carry set on itself, and then ends propagation.
Subclasses of TKView
which handle key events but do not have complicated mouse event handling
are not required to reimplement the musclik_
method. Simply be setting their Accepts First
Key flag (df_afkey
flag of the dflags
property) they will take first key status when clicked.
Mouse Alternate Event (musalt_)
Input | Description |
---|---|
- | None |
Output | Description |
- | None |
Mouse event types which do not have their own method result in musalt_
being called. The
need to respond to these mouse events is relatively rare. Subclasses which need, for example,
to respond to right mouse up, or events relating to the middle button, can subclass musalt_
.
When musalt_
is called, read the low-level mouse event. Its type then needs to be checked
and types that you're not interested in can be ignored.
It takes extra work to read the mouse event, check its type, and branch based on what type it
is. This work is unnecessary when, say, the musdclik_
method is called. When that method is
called you know that the current mouse event is a left double click. And you only subclass
musdclik_
if you have a specific reason to handle that event. The musalt_
method is a tradeoff;
it's more difficult to process rare event types, but it saves memory in every class.
Node Methods
Method | Description | Offset |
---|---|---|
addchild_ | Add child node | 40 |
unparent_ | Remove THIS from its parent node | 43 |
Add child node (addchild_)
Input | Description |
---|---|
RegPtr | Child node to add to this node |
Output | Description |
- | None |
The user interface hierarchy is established by the node properties of TKView
. Every TKView
has
a parent
, child
and sibling
pointer. Every node, except the root node, has one parent. A node
may have one or more child nodes, but has only one child
property. The child
pointer is to the
parent's first child. The way that parent is related to its second child is via its first
child's sibling
pointer.
Node tree pointer property relationships
The tree can be built by calling the addchild_
method on a node, (i.e., a TKView
,) and passing
a RegPtr to the child node. The child node can be any TKView
or subclass of TKView
. The
addchild_
method handles establishing all of the node property pointers. For example, if the
node passed in is the first child, this.child is updated to point at the child, and the
child.parent is updated to point at this node. If this is a second, third, or Nth child, the
first child is looked up and its sibling
pointers are followed to find the last child. The node
passed in is connected via the sibling
pointer of that node, and the child.parent is set to this
node.
Subclassing addchild_
is useful when the class wants to limit or keep track of when and how
many children are being added. For example, a TKSplit only supports two children. If a third
child is attempted to be added it raises an exception. After verifying that another child is
allowed to be added, or some other check such as verifying that the child is of an appropriate
class, the actual work of manipulating the node pointers can still be performed by calling the
superclass addchild_
method.
Remove THIS from its parent node (unparent_)
Input | Description |
---|---|
- | None |
Output | Description |
- | None |
It is possible to reorganized the user interface's node tree at runtime. A node can be removed from its parent node and then added as the child of a different parent. The relationships between a node and its own children are unaffected when the node is removed from its parent. In this way an entire branch of the node tree can be moved. This could be used, for example, to reverse the order of the two sides of a TKSplit, by removing both children and then adding them back in the reverse order.
The process of removing a node from its parent is called unparenting. The unparent_
method is
called on the node you want to remove from its parent. All of the node pointers are updated
accordingly. If this node was in the middle of a sibling chain, the previous sibling's sibling
pointer is first updated to this node's sibling
pointer. And if this node was the parent's
first child, then the parent's child
pointer is updated to what was this node's sibling.
There is a check when calling unparent_
, if the node has no parent then nothing happens.
There are no checks, however, when calling addchild_
. It is very important that you not add
one node as the child of multiple parents, or unexpected things will happen and a crash or an
infinite loop will probably follow. If a node is already attached to the node tree, call
unparent_
before adding it with addchild_
to a different place in the node tree.
Sizing Methods
Method | Description | Offset |
---|---|---|
resize_ | Resize Node | 46 |
contsz_ | Get Content Size | 49 |
Resize Node (resize_)
The resize_
method resizing the current node relative to its parent node while taking into
consideration its anchoring, offset, and resize mask properties. Resizing is done automatically
as part of the Toolkit update cycle. You should never manually call resize_
on any node.
Subclassing resize_
is useful for creating custom container classes.
Get Content Size (contsz_)
The contsz_
method is called when a container class needs to know the size of its content
class. You should have no reason to manually call this method. It is called by other classes
on their own child, usually to adjust scroll bar representations of the size of the content.
Display Methods
Method | Description | Offset |
---|---|---|
update_ | Update computed properties | 52 |
draw_ | Draw the view's appearance | 55 |
Update computed properties (update_)
The update_
method is called automatically during the Toolkit update cycle. You should never
call the update_
method manually. This method handles updating computed properties, and
recursively traverses the visible node tree.
For example, in order to save time, if a node in the tree is out of bounds or not visible when
its parent is resized the resizing logic on that branch of the tree is skipped. When this branch
of the tree later becomes visible, or if its parent's bounds or scroll offsets are adjusted, the
new bounds have to be computed and deferred resizing may have to be applied. This is performed
by update_
and it refers to the object's modified flags (mflags
) property to determine what
work needs to be done.
Draw the view's appearance (draw_)
The draw_
method is called automatically during the Toolkit draw cycle. You should never
call the draw_
method manually. When the draw_
method gets called, the appropriate draw context
has already been configured.
If an object's appearence changes and it needs to be redrawn, the df_dirty
flag must be set
on the TKView
's display flags (dflags
) property. Additionally, the tf_dirty
flag must be set on
the Toolkit environment's flags (te_flags
) property. The global dirty flag on the Toolkit
environment is necessary to inform the Toolkit update cycle that something within the view
hierarchy needs to be updated and drawn. Recursively searching the view hierarchy for a node
in need of servicing is expensive, and so it is only performed when the global tf_dirty
flag
is set on the environment.
TKView
's draw_
method performs the important task of determining which of its own children are
visible and inbounds, and then configuring the draw context for each child and calling each
child's draw_
method. This process is recursive.
The draw_
method is usually reimplemented by a subclass to draw its custom appearance.
However, if the subclass supports child views, it is highly recommended to call the
superclass draw_
method to handle drawing those children, rather than reimplementing the
complex logic provided by TKView
.
Event Methods
Method | Description | Offset |
---|---|---|
hittest_ | Mouse Event Hit Testing | 58 |
Mouse Event Hit Testing (hittest_)
Hit testing is the procedure by which a low-level mouse event is mapped on to the physical
layout of the hierarchical user interface. You should never need to call hittest_
manually.
When certain initializing mouse events occur, such as a wheel event or a left mouse down event,
hittest_
is called on the root view by a Toolkit KERNAL routine.
The low-level event's pixel coordinates are converted to row/column coordinates with
mouserc.
These are then normalized to the Toolkit environment's offset on the screen, using its te_posx
and te_posy
properties. These coordinates are then checked to see if they are overtop of the
root view. If they are, the coordinates are saved to the root view's hitrow
and hitcol
properties.
The hittest_
method of the root view then checks each of its child views for visibility and
in bounds, and if the child is under the coordinates of the hit, it renormalizes the event
coordinates for the child's offset and calls the hittest_
method on that child view. The new
renormalized coordinates are saved on that view's hitrow
and hitcol
property. This process
continues recursively until the last hit child is found. The recursion unrolls returning a
pointer to the hit view. The pointer to the hit view is saved to the Toolkit environment's
first mouse view (te_fmusv
) property.
Only some event types initiate a hit test procedure. For example, a mouse move event and a mouse click event do not get routed to the view above which the mouse pointer was positioned, but are sent to the Toolkit environment's first mouse view instead. Although this may seem unintuitive, the resultant behavior is much more natural than if hit testing were performed for every event type. This allows you, for example, to mouse down on a scroll bar's nub and then drag the mouse up and down, and the nub continues to track the mouse's vertical position even when the pointer is no longer above the scroll bar view.
To track the event with pixel precision, the low-level mouse event can be fetched with
readmouse. Its least
significant bits can be consulted to get the exact pixel offset within the renormalized
row/column cell coordinates that are found in the hitrow
/hitcol
properties of the view.
The Accepts First Mouse (df_afmus
) flag of TKView
's display flags (dflags
) property is used
by hittest_
to determine whether a TKView
is sensitive to mouse events. By unsetting its
df_afmus
flag, the view (and consequently all of its child views) become transparent to mouse
events. df_afmus
is set by default when a TKView
is initialized.
Object Definition
//os/tk/s/:tkview.s Object Size: 39 (+3) bytes
Responder Properties
Property | Description | Size | Offset |
---|---|---|---|
nextresp | Next Responder | 2 | 2 |
Next Responder (nextresp)
By default this property is initialized to null. Key events are propagated to the next responder if it is set. Otherwise they are propagated to the parent node.
You do typically need get or set this property manually. It is used for forming complex event propagation relationships between closely collaborating classes, such as a TKScroll view and its nested TKSBar controls
Node Properties
Property | Description | Size | Offset |
---|---|---|---|
parent | Parent Node | 2 | 4 |
child | First Child Node | 2 | 6 |
sibling | Next Sibling Node | 2 | 8 |
Property | Description | Size | Offset |
tag | Identifying Tag | 1 | 10 |
Parent Node (parent)
This property is set when this object is added to the node tree by passing a pointer to it
when calling the addchild_
method of the object that is to become this nodes parent.
It can be useful to read the parent
property when you have a pointer to a node but want to
get a relative reference to its parent node.
First Child Node (child)
This property is set the first time the addchild_
method is called on a node. The pointer to
the child node is saved to the child
property.
It can be useful to read the child
property when you have a pointer to a node but want to
get a relative reference to its first child node.
Next Sibling Node (sibling)
This property is set when this object acquires its first new sibling. When a node is passed
to the addchild_
method of this node's parent, and this node is currently the last sibling in
the sibling chain, then this node's sibling
property is set to the new sibling.
The sibling
property is useful for counting how many children a node has, or for finding the
Nth or last child of a node. Finding a node's first child is performed by reading its child
property. To find it's second child you would read the first child's sibling
property. To
find the last child, continue to follow the sibling
pointers until you reach a node whose
sibling
pointer is null.
Identifying Tag (tag)
The tag
property is a convenient way to identify a node. It is very open and undefined and
can be put to any creative use.
One example of its possible use is when two or more objects share a callback. It is often
easier to read the tag
from the this object than it is to try to identify the object by its
this pointer. The callback can then do different things depending on the tag
's value.
Another common use is to find a view somewhere in the view hierarchy by its tag
. This can
be done with viewwtag. The View With
Tag routine only searches from the start node down. To
search the entire tree the root view can be passed as the start node. However, tags can be
reused, the same tag
found on more than one object, and View With Tag can still be used to
find it, by starting somewhere other than the root node.
Sizing Properties
Property | Description | Size | Offset |
---|---|---|---|
offtop | Offset Top | 2 | 11 |
offbot | Offset Bottom | 2 | 13 |
absbot | Absolute Bottom | 2 | 15 |
height | Height | 2 | 17 |
Property | Description | Size | Offset |
offleft | Offset Left | 2 | 19 |
offrght | Offset Right | 2 | 21 |
absrght | Absolute Right | 2 | 23 |
width | Width | 2 | 25 |
Property | Description | Size | Offset |
rsmask | Resize Mask | 1 | 27 |
The sizing properties are used during the Toolkit update cycle and adjusted by the object's
resize_
method to dynamically change the position and size of the view relative to its parent
view. These properties are used again prior to the object's draw_
method to configure the draw
context. The draw_
method can read these properties to help it decide how to draw itself.
Alternatively the draw_
method can simply start outputting its appearance using the standard
context drawing KERNAL routines and allow the context to figure out where those characters end
up on the screen. The context automatically clips the output and provides feedback to the
draw routine about, for example, when further output to the current row or column will no longer
have any effect on the screen. This feedback can help make the draw_
method more efficient.
Resize Mask (rsmask)
The resize mask is a set of bits which determine how a view gets resized when its parent view
gets resized. These are its top, buttom, left and right anchor bits (rm_ankt
, rm_ankb
, rm_ankl
,
and rm_ankr
) defined by //os/s/:toolkit.s. The resize mask has one additional bit, resize
children (rm_rschd
). If the resize children bit is clear, then when a view's resize_
method
is called, it does not recursively call the resize_
method on any of its children.
A container view has two broad conceptual layout modes: constrained and unconstrained. How a container's children are anchored and positioned within it depends on whether it is meant to be constrained or unconstrained. Although it is possible to configure different child views of the same parent using both of these broad conceptual modes, the result is unproductive and could lead to glitches in the layout, hit testing, drawing or all three.
Constrained Layout
A constrained layout is when a container has a fixed set of child views, with a fixed relationship to one another but a flexible size. When the container is resized the size and position of the child views react, becoming either larger or smaller themselves to fit the space.
For example, imagine you have a user interface that has a list with two buttons below it. One button on the left and another on the right. When the container view is resized, there will still be a list at the top with two buttons below, one on the left and one on the right. However, the list will get bigger or smaller in both dimensions, the buttons will stick to the bottom getting closer or further from the top, and the buttons will stay the same width but will get closer or further apart. See the following diagram.
Example of a constrained layout
In this "constrained" layout, the list (blue) is anchored to the top, bottom, left and right. But its bottom edge is offset from the edge of its parent by some number of rows to make room for the buttons. When the container (yellow) is resized the height and width properties of the list are recomputed dynamically, as is the absolute bottom.
The left button (green) is anchored bottom and left, and it has a fixed width. When the container (yellow) is resized, its offset top and its absolute right are recomputed dynamically. The right button (purple) is anchored bottom and right, and it too has a fixed width. When the container (yellow) is resized, its offset top and its offset left are computed dynamically. In other words, the way that a view is anchored determines which of its other properties are dynamically computed when its parent is resized.
In a constrained layout, views with a fixed height or width should be anchored to the top or bottom but not both, or anchored to the left or right but not both. Any view that should flex in a given dimension should be anchored to both sides of that dimension, but can have offsets to make room for something fixed to be positioned above, below or to the side.
TKView
does not have the ability to dynamically resize two views in proportion to one another.
For example, TKView
cannot have one child that is 33% of its width and another child view beside
it that is 66% of its width, such that as the container is resized the children maintain this
ratio. If this were required, it could be accomplished with a custom subclass of TKView
.
Unconstrained Layout
An unconstrained layout is when a container has one or more child views which either stack up vertically, or across horizontally, or both. When the container is resized, the child views maintain their size and position but may overflow the visible bounds of the container.
For example, imagine a container that holds a label next to an input field. Below that in the stack comes another label and another input field. Below that perhaps comes a set of checkboxes each with a label. Then below that perhaps a multi line text area with a button below it. This layout is called unconstrained, because the number of child views and how much space they occupy is not constrained by the size of the containing view. See the following diagram.
Example of an unconstrained layout
In this "unconstrained" layout, the container (yellow) has 4 child views (blue) which are much larger than itself. The child views extend out horizontally and vertically past the visible bounds of the container and into a virtual space (grey) that may in fact be much larger than the screen.
The child views of a container in an unconstrained layout are typically always anchored top and left, each with a fixed height and width. Each child view's offset top is used to stack them one after the next, vertically.
If the content size (contsz_
) method is called on the container, TKView
checks the sizes and
positions of all of its visible children, and computes the size of the grey rectangle dynamically
so that it minimally fits all of the children and then returns that as its content size.
Calling the content size (contsz_
) method on a container with a constrained layout, on the other
hand, will always return a size equal to the container's own current size, because the nature of
a constrained layout is that all of the children resize themselves to fit within their container.
Typically a container with an unconstrained layout will be set as the content view a TKScroll
view. The TKScroll view calls contsz_
and adjusts its scroll bars to allow the user to move that
large virtual grey square around so that different parts of it are visible through the smaller
area of the container.
Mixing Layout Concepts
In most practical user interfaces constrained and unconstrained layouts are combined together. In the diagram showing the example constrained layout, above, the list (blue) itself is a kind of unconstrained layout. The list may have hundreds of items each of which is stacked one after the other. The list is typically added to a TKScroll view, so the full content of a long list can be viewed. The TKScroll view is anchored top, bottom, left and right in its container, but the TKList itself is only anchored top and left within the TKScroll.
Less common, although entirely possible, is to treat one of the child views of an unconstrained layout as itself the container of a constrained layout.
There is a way of mixing layouts that does not work and will produce erroneous calculations
and glitchy drawing. Imagine a container with an unconstrained layout, with many child views
stacked up vertically which push past the visible vertical height of the container. To then
add another child to that container and anchor the child to the bottom, is invalid. Although
it might make some conceptual sense, TKView
cannot compute its size or position properly.
Lastly, a TKView
may validly mix layout concepts in two different dimensions. For example,
a TKView
may have a constrained layout horizontally, with children that are anchored left
and right and resize to fit the available horizontal space without overflowing, and at the
same time have an unconstrained layout vertically. A typical use case for this is a form
with a large set of controls that each have a fixed vertical height that are stacked one
after the next, down and off the bottom edge of the container view, but which horizontally
are anchored left and right so they all dynamically fit the container's width.
This mix of constrained and unconstrained will also typically be embedded in a TKScroll view.
It is possible to enable only one scrollbar to handle the unconstrained dimension. When the
TKScroll calls the container's contsz_
method, it will correctly compute that the content width
is the current width of the TKView
, and the content height will be computed to contain all of
of the children.
Scrolling Properties
Property | Description | Size | Offset |
---|---|---|---|
s_top | Scroll Top Offset | 2 | 28 |
s_left | Scroll Left Offset | 2 | 30 |
Every view's visible rectangle is a view port into large virtual space. The scroll properties
define where inside that virtual space the view's visible rectangle is oriented. This is
accomplished by the s_top
and s_left
properties being set into the draw context for the
current view prior to its draw_
method being called.
When the view draws using the draw context it sets the local row and column of where to start
drawing. This coordinate is automatically computed relative to the draw context's scroll
offsets. In a subclass of TKView
that handles drawing large content, such as TKList, the
s_top
property is used to determine the index of the top visible list item. The view can draw
more efficiently by only starting to draw from the currently visible content.
When TKView
has multiple children, it consults its own s_top
and s_left
properties to determine
which of its children are within its visible bounds. Anything outside its visible bounds gets
flagged for requiring updates, such as resizes, but the work is deferred until those children
are scrolled into the visible area.
The TKScroll view and its TKSBar controls are typically used to manage the s_top
and s_left
properties of the TKScroll's content view. However, it is also possible to programmatically
adjust the scroll properties. If the scroll properties are adjusted manually, it is necessary
to set the bounds check bit (mf_bdchk
) of the view's modified flags (mflags
.) This informs
TKView
that it needs to perform a bounds check on all of its child views.
Drawing Properties
Property | Description | Size | Offset |
---|---|---|---|
dflags | Display Flags | 1 | 32 |
mflags | Modified Flags | 1 | 33 |
bcolor | Background Color | 1 | 34 |
Display Flags (dflags)
The display flags property holds a set of 8 bit flags. These are mainly used to assist in drawing the view, but also have role in event processing.
Constant | Value | Note |
---|---|---|
df_dirty | %0000 0001 | Flags the view to be redrawn |
df_sized | %0000 0010 | Indicates that the view's sizing properties are up to date |
df_opaqu | %0000 0100 | TKView's draw_ method performs a context clear before redrawing |
df_afkey | %0000 1000 | The view accepts first key status |
df_afmus | %0001 0000 | The view accepts processes and propagates mouse events |
df_first | %0010 0000 | The view is the first key view |
df_ibnds | %0100 0000 | The view is inside the visible bounds of its parent |
df_visib | %1000 0000 | The view is visible |
The most common flag that needs to be set manually is df_dirty
. If any property that affects
drawing is manually changed, df_dirty
needs to be set (along with the Toolkit environment's
te_flags
tf_dirty
flag) in order for the view to have its draw_
method called on the next
draw cycle.
df_opaqu
is typically disable, but if the children of a TKView
do not completely cover its
visible area, and it does not have an opaque ancestor, then it's df_opaqu
flag may need to be
set to prevent a visual ghosting.
df_afkey
is typically disabled. Custom subclasses of TKView
which are capable of handling
key events should enable this flag.
df_afmus
is enabled by default so that this view participates in event propagation. There are
certain circumstances under which you may want a view and its children to ignore mouse events.
That can be accomplished by enabling this flag.
df_first
is set if this view is currently the first key view. Some subclasses, such as TKInput,
modify their appearance when this flag is set. This allows the text field in focus to stand out
amoung a set of other text fields. Key events are automatically routed first to the view with
this flag set. You should never manually set this flag. To affect this flag, call the setfirst_
method.
The df_visib
flag determines the visibility of the view. If this flag is unset the view will
not be drawn and it will not participate in mouse or key event handling. Sizing and bounds
checking are deferred until the view becomes visible again.
Lastly, the df_ibnds
and df_sized
flags are computed internally and should not be adjusted
manually.
Modified Flags (mflags)
The modified flags property holds special flags that are used to indicate when resizing and
bounds checking need to be performed on a view. If a parent view is scrolled, it recursively
calls bounds check on each of its children. However, if one of its children is not visible,
the bounds check does not recurse into that branch of the view hierarchy. Instead the
mf_bdchk
flag is set on the mflags
property.
If a parent view is resized, it recursively calls resize_
on each of its children. However, if
one of its children is either invisible or out of bounds, the resize does not recurse into that
branch of the view hierarchy. Instead the mf_resiz
flag is set.
When a view becomes visible, and one of its modified flags is set, the bounds check or resize is performed just prior to redraw during the update cycle. It is possible that a view which becomes visible itself has an invisible child view. In that case, the bounds or size check is performed on it, but the modified flags are set again on its own invisible child.
If any of a view's sizing properties are manually modified, its resize (mf_resiz
) flag
should be set on the modified flags (mflags
) property.
If a view's scroll properties are manually modified, it's bounds check (mf_bdchk
) flag
should be set on the modified flags (mflags
) property.
Hit Test Properties
Property | Description | Size | Offset |
---|---|---|---|
hitrow | Hit Row | 2 | 35 |
hitcol | Hit Column | 2 | 37 |
The hitrow
and hitcol
properties can be read to get the coordinates of a mouse event resulting
from the last hit test. These properties have been normalized to the coordinates of this view.
Custom subclasses of TKView
can use these properties to determine where within the view the
event hit.
For example, when a standard TKButton is clicked, where the click occured within the button is not important. However, a subclass that draws itself as a stepper control (up and down arrows beside a value) may ignore events that happen over the value and perform different behaviors if the event is over the up or the down arrow.
Relationships
Inherits from: TKObj
Parent Section: Class Reference
Next Section: Subclassing
Next Chapter: Writing an Application
Table of Contents
This document is subject to revision updates.
Last modified: Jul 02, 2024