четверг, 30 августа 2012 г.

воскресенье, 7 августа 2011 г.

Presenter

Central component of MVP is presenter.
Presenter adds model data on view and handle it events.
Each presenter has view. View know nothing about presenter and model.
View is basic morph which show data on screen.

Primitive presenters change internal state of morph (view). For example, PtyTextLabelPesenter changes text contents of PtyTextMorph (view of text label presenter) to show it string model.
Composite presenters (which usually implemented in applications) add another morphs to view from child presenters. Each presenter can show other presenters on view.

To implement new presenter you should subclass PtyCompositePresenter and implement method #showViewItems. Inside this method you can add child items on view:

PtyCompositePresenter subclass: #ContactPresenter
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'PresentyExamples-ContactManager'

PtyContactPresenter>>showViewItems

    self showItem: (PtySimpleLabelPresenter on: model firstName).
    self showItem: (PtySimpleLabelPresenter on: model lastName)

This is ContactPresenter for Contact instances.
Two messages can be used to show child presenters: showItem:, showItem:on:. First message show child on own presenter view. Second message show child on concrete panel.
You can request subpanels (submorphs) from view by "view panelNamed: 'panelName'". It searchs morph with given name inside structure of view morph (recursively). If target morph absent it will be created and added to view morph.

All presenters can be created on concrete model by #on: message.

When you implement presenter you don't need to specify view for it. View of presenter is created by guide when you request guide to show presenter (method #showItems: implemented by guide). Guide search appropriate prototype for presenter and creates view based on it.
When prototype is absent default view is created. Default view is unconfigurable morph with red/yellow colors. You can design it appearance (by morphic features) and save it as prototype for presenter view (in halo menu there is "apply prototype changes" item).
Prototype is basic morph. Views are created by simple copy (#veryDeepCopy) of prototype.

Presenter can have specific prototypes for concrete context of applcation:
  • it can be specific inside different parent presenters
  • it can be specific in context of different tasks
  • it can be specific for different styles of presenter
  • it can be specific inside different parent presenter styles
Thus appearance of presenter view can be different in different parts of application. It can be configured without programming.

Each presenter can have style. You can create any presenter with style by:
  • PresenterClass on: aModel withStyle: #styleName
You can change presenter style at run time. For example, input field in Presenty change it style between  #filled and #unfilled according to filling state of field input:

PtyFieldEditorPresenter>>valueChanged
    | inputStyle |

    inputStyle := model isValid ifTrue: [#filled] ifFalse: [#unfilled].
    guide show: input withStyle: inputStyle

Last expression change style of presenter "input". It view will be replaced with new view based on new prototype.

All presenter prototypes stored in instance of PtyPrototypeManager. You can get it by:
  • guide uiBuilder prototypesManager
You can store this object to file and share between images:
  • (FileStream newFileNamed: 'yourApp.skin') fileOutClass: nil andObject: guide uiBuilder prototypesManager.
  • guide uiBuilder prototypesManager: (FileStream fileNamed: 'smallkiosk.skin') fileInObjectAndCode.

Next screencast show how implement presenters: http://www.youtube.com/watch?v=f3hk3fLBcsE&feature=player_embedded




Basic concepts

Presenty is a user interface framework that makes it easy to create reusable components of user interface and user interraction logic. With Presenty you can lively tune appearance of your application. 

Presenty based on Model-View-Presenter pattern, view prototypes and continuations.

MVP is powerfull pattern for implementing reusable components of user interface. With MVP you can present your model by different presenters in concrete place of your application. Unfortunately existed MVP frameworks make it difficult to create multiple views for same presenter. For example, you implement ContactPresenter. And you want specific view for it in "contact dial task" and "contact sms task". With Presenty you take it easy. Indeed you can show your presenter with styles (selected or deselected for example). And appearace of this styles can be specific in concrete application task.

With Presenty you can create tasks - reusable components of user interraction logic. Implementation of tasks based on continuation (like tasks in Seaside). Task programming based on simple DSL (it is usual smalltalk of course) which gives you expressions for request confirmations, show warnings, request requisities, request new tasks and others:
  • user warn: 'message'
  • user confirm: 'question?'
  • user input: requisities
  • user select: 'ItemName' from: itemsArray.
  • user execute: newTask
  • ...
You can extend this DSL for your application. For example, you can implement "user dialTo: aContact" which will request user for new DialContactTask.

Every task know user and guide (PtyUser and PtyGuide). With "guide" you can control current view area. You can set up new area or add new items to it. You can set up modal view area and all ui actions will be placed in modal mode:

   guide doInModal: [user warn: 'message'].
Guide is central object which known by every application component: tasks and presenters. It is like museum guide which show you exhibits. You can ask guide to show you presenters or execute some tasks.

Guide has viewport. It is instance of PtyViewportPresenter which presents main application panel with current view area. When you ask guide to show some presenter guide placed it on current view area of viewport.

Each task has its own view area in which it placed ui components. Task view area inherits from caller (parent) task view area. So all components of caller tasks become parts of new task.
Task can return answer to parent (caller) continuation by "parent resumeWith: #answer".
Or you can stop current task by "user stopCurrentTask". And user will be return to previous request.

Each presenter know about continuation in which context it has been shown. Presenter holds it in context variable. All presenters can resume it context with some value by "self answer: #value".

Guide has taskNavigator object. It saves all user requests. So you always can add back button to view area which return user to previous request. "User stopCurrentTask" implemented by taskNavigator too. It removes from navigator all tasks derived from current and executes last remain task which repeats last user request.

Presenty support multilingual by default. All text and image labels are translated by guide translator object. All kind of labels has label name which is key for translator dictionary. You can easily add new languages and translations for it. When image label shown it requests image from guide resourcesManager by translated label name key. By default guide resourcesManager is PtyResourcesManager which loads images from "images" folder near VM.

Guide has task settings (PtyTaskSettings). Task settings holds settings of tasks and presenters. By "guide create: aTask" you can create task instance with saved settings. New instance can be another class. For example, you can request PtyShowListTask and guide creates PtyShowListWithPagesTask or PtySimpleShowListTask. You can see hierarchy of this kind of task. Besides you can spesify settings in concrete context of current tasks chain. So in one context you will see list of items splitted by pages. In another task you will see items in scrolled panel.
Task settings has userFeeling object. It provides preferred instances of tasks and presenters for current platform. Now Presenty has PtyDesktopUserFeeling and PtyStreetUserFeeling. It is possible to implement PtyMobileTouchScreenUserFeeling which gives you input fields with visual keyboard. (It is really done by PtyStreetUserFeeling, but PtyStreetUserFeeling purpose is little different than mobile touch screen devices)

With Presenty you can program in terms of user interraction logic (based on tasks). Way in which this logic will be presented to user is part of design time and configuration of application. For desktop platform you can configure one "look and feel". For tablet platform "look and feel" can be very different. With Presenty you take it without code changes.

In next posts I will show how create Presenty components of user interface.

Overview


Presenty is framework for building applications with rich and complex user interface. It brings together several ideas, concepts and technologies including:
  • Modified Model-View-Presenter pattern
  • Continuation-based tasks as elements of user interaction at application level
  • User requests as building blocks for tasks:
    • Primitive user requests to invoke base interface components
    • Compound requests for calling other tasks
  • Extendable DSL (pure Smalltalk) facilitating task implementation
  • Prototyping for visual components and tasks

Presenty provides appearance and behavior configuration for components and tasks without application code modification.

Presenty license is MIT and run on Pharo.

Presenty developed on Pharo 1.1.1 and not tested on latest versions yet.
You can load it from http://www.squeaksource.com/Presenty.html. Presenty has dependence from seaside continuations. So you must load Seaside-Pharo-Continuation and Grease packages from http://www.squeaksource.com/Seaside30 repository before you install Presenty. If you want PresentyTests you should load Mocketry package from http://www.squeaksource.com/Mocketry