Detail View Documentation Notes
Below, I've started the detail view & attribute editor documentation I'm supposed to produce. First, meta-documentation...
I expect that the most useful way to discuss this stuff is to talk about how the detail view works first, then do the same for attribute editors. Once that's done, I can explain how developers should add their own DV subtree, then (if necessary) how they should implement their own attribute editors.
However, there are important issues that aren't clear in my own mind (and therefore won't be clear in this documentation):
- I'm not sure how to convey what we expect from developers with regard to mixins: Should they always plan on being mixed with Note? or Never? etc. Where do new items fit into stamping? How should developers avoid attribute conflicts?
- There are inherent conflicts around the 'body' attribute, if you're not mixed with Note (which has one and wants it to be a String). I reported bug 3531 about this, but it's tagged 'future'.
- There are inherent conflicts around attribute redirection:
- Redirecting a common attribute (eg, 'about') to different attributes of different mixin types means that the value of an attribute can change or be lost as the result of stamping (that is, the subject of an email is stored in a different attribute than the title of a note: Stamping a Note as an email causes the Note's subject to be hidden (and replaced by the email's 'subject' attribute, which is empty by default); unstamping emailness from an Email causes the subject to be lost (bug 2507).
- It's not clear how we'll consistently allow editing of redirected attributes in columns of a summary view: for instance, 'who' is sometimes editable (when it's the 'to' address list of an outgoing email) but not freely editable when it's the 'from' address (when it should only be a configured account's identity).
- These issues make it hard to explain this stuff... or even to implement it, causing a number of reported bugs:
- bug 1745: Use displayName for item titles?
- bug 2167: Who, Date, not always showing the right attribute
- bug 2481: Shouldn't the 'who' column for incoming mail be the sender's email address?
- bug 2605: inline edit of date in summary window wipes the entire entry
- bug 2767: displayName attribute value (or selection behavior) is wrong
- bug 3735: Stamping Event as Task resets alarm to None
Overview
You're someone interested in adding functionality to Chandler and wondering how to do it in a way that fits well with the rest of Chandler's architecture, and you're here to find out about the
detail view, that space on the right side of Chandler's window that presents the individual attributes of a selected item for editing. My assumption is that you've already know a couple of things:
- enough about CPIA to know what a block is,
- enough about the content model to know what a ContentItem? is, and how stamping works.
The detail view wants to remain general: it wants to remain uncoupled from the displayed item and its attributes so that you can add new kinds of items without making changes to the detail view. It does this in two ways:
- The detail view avoids knowing too much about what the item is by letting an associated structure, the detail view subtree, specify the blocks associated with a particular mixin Kind.
- It avoids knowing about what the item's attributes' types are by using attribute editors to present the individual attribute values.
This page talks about how these mechanisms work and fit together. After you've read it, you'll be able to get the detail view to display a new kind of item, and you'll be on the road to understanding how different types of attributes are presented.
A few conventions:
- I've Capitalized terms that have specific meaning in Chandler-land, like Block, Kind, and ContentItem.
- I've italicized terms that I'm introducing, so pay attention.
ContentItem, mixins, and stamping
To talk about how the detail view works, it's useful to explain a little about what ContentItems are, in general terms:
- A ContentItem is an object, persisted in the Repository (so it's an Item), that represents a user-visible thing, like a Note, a Message, or an Event.
- Each ContentItem has a Kind that gives it certain attributes, like the way a Note has a 'title' and a 'body', and an Event has a 'startTime'.
In actuality, a ContentItem's Kind is usually a list of several Kinds mixed together, each of which defines several attributes appropriate for that Kind: almost always, there's Note (which brings attributes like 'title' and 'body'), and potentially others "mixed in" that give additional attributes and behavior, such as MailMessageMixin (which adds 'to', 'from', etc).
Chandler's user actually gets to manipulate this list of Kinds: when an item is displayed in the detail view, the user can add or remove Kinds using the markup bar buttons for email-ness, task-ness, and event-ness. When the user clicks on one of these buttons to add or remove a Kind, that Kind's attributes are added or removed from the displayed item.
(There's a lot more to know about ContentItems, Items and the Repository in general...
link here to more details elsewhere)
(You can define new mixin kinds, but we haven't figured out how best to have you modify the user interface for stamping yet. I'm not even sure how to tell you about whether you'll only create new things to mix in with Note, or whether you'll create things that aren't Notes.)
The detail trunk subtree
Like any other CPIA view, the detail view is composed of a hierarchy of
Blocks. The detail view is different in that it assembles its tree of blocks when it's told to display a particular ContentItem.
It has to assemble its tree of blocks dynamically, because the detail view doesn't want to know about specific Kinds (so that it remains able to show new kinds of items that haven't been thought of yet), and because the stamping mechanism allows all sorts of permutations of different Kinds to combine in one ContentItem.
So, when the detail view is told to display an item, it looks at that item's Kinds, and for an associated data structure with an awful name called a
DetailTrunkSubtree. This DetailTrunkSubtree maps a Kind to a list of blocks that should be included when an item with that Kind is displayed, and each block in that list has a 'position' attribute. Here's an example - here are example DetailTrunkSubtrees for Note and MailMessageMixin:
(If you actually look at the real definition of these DetailTrunkSubtrees, you'll see that I omitted a few spacer blocks; you'll also see that the 'position' attributes are on the referenced blocks, not in the DetailTrunkSubtree definitions themselves. Don't let this hang you up.)
The detail view finds the DetailTrunkSubtrees associated with the item to be displayed (for each DetailTrunkSubtree's Kind key, it asks the item if it's an instance of that Kind), and combines all of the blocks referenced by those DetailTrunkSubtrees' rootBlocks lists into one big list, then sorts that list by 'position'. This produces a list of block trees to be at the top level of the detail view for that item: the detail view makes a copy of each of those blocks, assembles the copies in position order, and that's the detail view for that item. (It then caches that tree of blocks, so that if asked to display another item of the exact same combination of Kinds, it won't have to re-do this work.)
So, when displaying a MailMessage item (which is a predefined ContentItem subclass that inherits from Note and MailMessageMixin), this is the tree of blocks that would result (I've included the 'position's just for clarity - they're ignored after the sort is done):
| from | block | position |
| NoteSubtree | markupBar | 0.0 |
| MailSubtree | fromArea | 0.1 |
| MailSubtree | toArea | 0.11 |
| NoteSubtree | headlineArea | 0.5 |
| NoteSubtree | notesBlock | 0.9 |
| MailSubtree | attachmentArea | 0.91 |
Attribute Editors
Attribute editors present attributes of ContentItems and allow users to edit them. Whenever* an attribute needs to be displayed or edited, a mechanism selects a particular attribute editor class to present and/or manage the editing; this mechanism does so based on the data type of the attribute to be presented: a boolean gets the CheckboxAttributeEditor, a string gets StringAttributeEditor, etc.
(* "Whenever" here means "everywhere except the calendar panel", which doesn't yet use an attribute editor when editing event titles. Someday, it might; in the meantime, attribute editors are used in the summary view, the detail view, and the sidebar.)
There are two places that attribute editors are used currently:
- in the detail view, a special block type, AEBlock, acts as the host for presenting and editing an attribute;
- in the table views (the summary and sidebar), the table itself looks up an attribute editor for each type that it has to present, and shares that editor among all the cells of that type. (This is all we'll say about this here, other than to mention that some of the weirdness of the attribute editor API comes from having to work in this model as well as the AEBlock usage.)
- (Eventually, I'm hopeful that the calendar canvas will use them too, but it doesn't currently. Also, we've discussed a dialog-box-building mechanism that would use them, but it doesn't exist yet.)
The presence of an AEBlock in a tree of blocks referenced by a DetailTrunkSubtree causes an attribute editor to be used there. The block definition of an AEBlock includes a 'viewAttribute' attribute, which specifies the name of the attribute of the selected item to be displayed or edited in that AEBlock. It can also optionally include a presentationStyle, which customizes the behavior of the selected attribute editor.
When AEBlock wants to find an attribute editor to use to present a particular item attribute, the data type of that attribute is detected dynamically: the type of the attribute's current value is checked first; if the attribute has no value (or the value is None), the repository's notion of the attribute (as returned by getAttributeAspect()), and if that fails, the schema definition (which supports
Calculated properties) is used.
To support this picking process, each Attribute Editor class has at least one corresponding
AttributeEditorMapping item in the repository; the AttributeEditorMapping maps its name (a type name) to a class name -- so the AttributeEditorMapping named "String" has a className attribute of "osaf.framework.attributeEditors.AttributeEditors.StringAttributeEditor". If one attribute editor class supports more than one type, it would have more than one AttributeEditorMapping item.
It's actually a bit more complicated than this... In an ideal world, data type would be enough to choose an editor; however, the picking mechanism is 'impure' in a couple of situations:
- When there are non-general requirements for behavior of a general datatype: for instance, we want to present a popup for time zone in the detail view, and it happens that the time zone on the item is stored as part of the start time - if we let the default behavior happen, we'd get a date-time string editor if we didn't override the default behavior.
- When the attribute value isn't a real attribute, or doesn't map easily to one. 'Calculated' properties (basically, Python properties that have a type name associated with them, just for attribute editors to look at) go part of the way toward solving this, though there are notification issues around them.
To resolve these impurities, an override mechanism is provided: The AEBlock's 'presentationStyle' can have a 'format' attribute, which causes the attribute-editor lookup process to only consider AttributeEditorMapping entries whose name ends with "+" and the format string. So, in the example above, one of the blocks referenced by the CalendarEventMixin DetailTrunkSubtree contains an AEBlock whose viewAttribute is 'startTime' (which turns out to be of type 'datetime') and whose format is 'timezoneOnly'. The attribute editor that we want used in this case is the TimeZoneAttributeEditor, so there's an AttributeEditorMapping named 'DateTime+timeZoneOnly' whose className is 'TimeZoneAttributeEditor'.
(more to come. Please don't edit the above - add any comments you'd like to make below, or send email. Thanks!)