Approach to ConversationListBox
Goal of this document is to describe the steps involved in moving from a Textview based widget to a ListBox based widget and to describe to the project what the plan behind the coming PRs and commits will be, also requested by @lovetox.
The document is stored in wiki form to allow discussion and mutual manipulation of the document.
For reference, here's the relevevant discussion from the Gajim MUC (where
erik == @ehuelsmann):
[11:22:39 PM] lovetox: erik, yes refactoring some things before working on the listbox is a good idea [11:23:14 PM] lovetox: its best you write down where you see potential for refactoring and let me look over it [11:23:58 PM] lovetox: so i can tell you into how much of a rabbit hole you are getting your self :) [11:24:48 PM] erik: lovetox: thanks. I've started some initial explorations in that area. Are you asking that I wrote something like a refactoring plan before going into the actual effort? [11:24:49 PM] lovetox: and of course the smaller the PR the faster it gets merged [11:24:56 PM] erik: I think that's fair [11:25:38 PM] erik: Ok. I'll write a bit of a plan and work on small commits along the plan. [11:26:04 PM] lovetox: yeah i think its usefull if you tell me what and where you want to refactor before you start it [11:33:25 PM] erik: I'll need a few days to get more of a grip on the code base.
High level assessment of the existing code base
ConversationTextview is tightly coupled with
HtmlTextview as it highly depends on the fact that the HtmlTextview is an editor: it directly accesses the editor buffer and uses it all over the place.
On the other side,
ChatControlBase depend on
ConversationTextview to display the list of messages, where
HistoryWindow uses some of the api provided by
ConversationTextview to populate the window and does some formatting of its own.
ChatControlBase leaves formatting of messages entirely to
The view performs the following functions:
- Maintaining a mapping of message IDs to text buffer positions
- Maintaining a mapping of 'time' to text buffer positions
- Maintaining a list of corrected messages (by message ID)
- Formatting message lines:
- Adding encryption status
- Adding a timestamp
- Adding the nick of the sender
- Scanning and formatting URLs in message text
- Formatting non-message lines (such as status messages, muc title, etc.)
- Deal with all user-interactions through the registration of various event handlers (popup menu, clicking of URLs, etc.)
HtmlTextview adds a single function to the above: parsing the provided content for
xhtml-im content (and cleaning non-compliant content).
In addition to
GroupChatControl also influences rendering of messages by selectively passing into the ConversationTextview the fact that a message has an XHTML-IM presentation.
ConversationTextview maintains the following API to achieve the formatting functions:
- (creation and updates of
HtmlTextview.get_buffer()-tags in various places)
For the mappings it maintains the following functions and attributes:
- correct_message [function]
- message_list [attribute]
- last_received_message_id [attribute]
HtmlTextview manages presentation with the following functions:
- display_html (which is also responsible for handling
From the summary above, it seems like
ConversationTextview is all of the model, the view and the controller: it maintains the data to be presented in all its aspects (model), it generates/manages presentation by virtue of the various
print_* methods (view) and it deals with various signals (e.g. user input) which affect presentation as well as other actions such as starting external browsers and all.
This mixture of roles of the
ConversationTextview complicates replacing the presentation of messages with a ListBox from a Textview.
Approach to moving to a listbox
- Separate the concerns currently concentrated in ConversationTextview a. Add new setup to this document a. Agree on changed separation of concerns with @lovetox (and others?)
- Gradually implement the new separation
- Based on separated concerns, design a ConversationListBoxview and ListBoxrow
- Implement minimal Listboxrow and ConversationListBoxview
- Synchronize APIs between ConversationListBoxview and ConversationTexview
- Based on some developer setting make listboxview and textview selectable
- Work on primary ListBoxrow until it's on-par with the current textview approach
- (merge the on-par replacement to master?)
- Develop specialized ListBoxrows for status messages, subject lines, etc., etc.
Requirements for a new design
High level functional requirements
- Allow for progressive introduction of improved presentation of various types of "messages" (rows)
- Allow swapping in/out of the current Textview based on developer need (and allow experimentation with other presentations in the future)
- Be on-par presentation-wise with the existing view (before being merged to master)
- Separate the concerns (at least) to be able to swap various presentation experiments quickly and easily
- managing the presented content & reacting to (user) input
- Building presentation logic which maps various types of messages to their respective ListBoxrow classes to allow differences per message(type)
(to be done)
In a chat with @lovetox, the following requirements were mentioned as well:
[04:46:27 PM] lovetox: first goals should be, the listbox should provide a way to add a message row, the message row itself should be a object that displays an avatar, the message, encryption symbol [04:47:25 PM] lovetox: the message row object should be based on some generic object, we will probably need many row objects that slightly differ [04:47:43 PM] lovetox: like some kind of status messages, error messages, info messages, etc [04:49:23 PM] wurstsalat: File transfers, image previews :)
Design for cleanup of the current state
Responsibilities of roles
The role of the presentation is to:
- Render chat lines coming in from the server
- Render chat lines going out to the server
- Render status updates from the client, server and chat channel (join/leave/etc)
- Update rendered lines when instructed to do so (corrected messages)
which means that the presentation will also need to:
- map message IDs to buffer regions
The role of the model (
ConversationContentModel) is to:
- add_message [function], to maintain:
- message_list [attribute]
- last_received_message_id [attribute]
- correct_message [function]
The role of the controller (also
ConversationTextview -- for now, but might need to be GroupChat/PrivateChat/ChatControl instead?) is to:
- respond to user interactions
- (open question: to network interactions as well? alternative: the model gets fed directly from the protocol stream)
Impact on the existing code base
print_* functions will to be moved from ConversationTextview to HtmlTextview. This impacts:
- (to be continued)
Design of the end state
In the end state, it should be easy to experiment with various presentations, where it's possible to swap presentations for each type of message ('status', 'error', 'info', 'incoming', 'outgoing', ...) separately. This will allow better future development and adaptation to user expectations.
- Presentation of both messages from the server stream as well as (informational) messages originating from the client
- Separation of management of content of the chat window from rendering of it
- Separation of presentation and chat management
- Efficient updating of chat content driven by the chat content management class
- Presentation layer will be provided all required input to determine correct rendering; currently this amounts to:
displaymarking(what's this about??)
xep0184(would have been more conveniently named
other_tags_for_name-- likely needs to move to the view/renderer
other_tags_for_time-- likely needs to move to the view/renderer
other_tags_for_text-- likely needs to move to the view/renderer
This value indicates whether a message comes from before the chat was opened (restored from history or retrieved from MAM)
What do all these mean???
These classes determine which messages are applicable to the current chat; they do nothing more than add the message to the ConversationContentModel.
This class is responsible of maintaining the exact content to be shown in the correct order. This means that any messages being corrected, get updated in this content representation. It's the responsibility of this model indicate to the view it has been updated/appended, including which messages were updated, by stanza_id. It is the responsibility of this class to store and update history of the channel (as this class is the place where it all gets together). Every line contained in the model has a unique ID to find it. In case a line doesn't have a stanza_id, this class will generate an id and return that to the caller.
This class-or-dict contains the available fields from the list above (can it just be a dict or do we need a class?)
- ConversationTextview (current presentation)
This class is responsible for showing any content available in the model and updating the display when the content model gets updated. It's up to the view to determine how any given message is to be rendered, meaning that the content model should be able to provide all data that might influence this (see the listed fields above for the current situation). The view maintains a mapping between stanza_ids and regions in the widget to ensure efficient updates. Note that this class and the next don't differ in their responsibilities.
- ConversationListBoxview (desired presentation)
Note that this design will allow - when the ContentModel will be connected to a source storing history - browsing back in history, if the content model gets extended to read messages from history upon request.