¿Por qué iba a querer utilizar un visor si las contribuciones de la UI del entorno de trabajo, como son las vistas, los editores, los asistentes y los diálogos, se pueden implementar directamente con los widgets de SWT?
Los visores le permiten crear widgets sin dejar de utilizar los objetos de su modelo. Si utiliza un widget de SWT directamente, tiene que convertir los objetos en las series y las imágenes que espera SWT. Los visores actúan como adaptadores en los widgets de SWT, manejando el código común para manejar los eventos de los widgets que, de lo contrario, tendría que implementar usted mismo.
Primero vimos un visor en la contribución de vista de la herramienta readme, en ReadmeSectionsView.
public void createPartControl(Composite parent) { viewer = new ListViewer(parent); ... }
Nota: los visores se pueden utilizar para proporcionar la implementación de las vistas y los editores del entorno de trabajo. El término visor no implica que los visores solo sean de utilidad para implementar las vistas. Por ejemplo, el visor TextViewer se utiliza en la implementación de muchos de los editores del entorno de trabajo y de los conectores.
JFace proporciona visores para la mayoría de los widgets no triviales de SWT. Los visores se suelen utilizar para los widgets de tipo lista, árbol, tabla y texto.
Cada visor lleva asociado un widget de SWT. Este widget se puede crear implícitamente, suministrando el objeto Composite padre en un práctico constructor de visor, o explícitamente, creándolo primero y suministrándolo al visor en su constructor.
Las listas, los árboles y las tablas comparten muchas funciones comunes desde el punto de vista del usuario, como las de poblar con objetos, seleccionar, ordenar y filtrar.
Estos visores conservan una lista de objetos de dominio (llamados elementos) y los visualizan en su correspondiente widget de SWT. Un visor de listas sabe cómo obtener una etiqueta de texto a partir de cualquier elemento de la lista. Obtiene la etiqueta a partir de una interfaz ILabelProvider, que se puede establecer en el visor. Sabe cómo correlacionar las llamadas de retorno del widget con el conjunto de elementos conocidos por el cliente del visor.
Los clientes que utilizan un widget de SWT sencillo tienen que operar a nivel de SWT, donde las series y los eventos suelen estar relacionados con un índice situado en la lista de series. Los visores proporcionan una semántica de nivel más alto. Los clientes reciben notificación de la selecciones y los cambios producidos en la lista mediante los elementos que proporcionan al visor. Los visores realizan todo el trabajo tedioso de correlacionar los índices de nuevo con los elementos, efectuando ajustes para obtener una vista filtrada de los objetos y reordenándolos en caso necesario.
La posibilidad de filtrado y ordenación se lleva a cabo designando para el visor un clasificador de visor (ViewerSorter) y/o un filtro de visor (ViewerFilter). (Estas clases también se pueden especificar para los visores de árboles y tablas además de para los visores de listas). El cliente solo tiene que proporcionar una clase que pueda comparar o filtrar los objetos de la lista. El visor maneja los detalles de poblar la lista de acuerdo con el orden y el filtro especificados, y mantiene el orden y el filtrado a medida que se añaden y eliminan elementos.
Los visores no están pensados para que el cliente los pueda ampliar. Para personalizar un visor, puede configurarlo con sus propios proveedores de contenido y etiquetas.
Un visor de lista (ListViewer) correlaciona los elementos de una lista con un control List de SWT.
Un visor de árbol (TreeViewer) visualiza los objetos de manera jerárquica en un widget Tree de SWT. Maneja los detalles para expandir y contraer elementos. Existen distintos tipos de visores de árboles para los diferentes controles de árbol de SWT (árbol sencillo, árbol de tabla, árbol de recuadro de selección).
Un visor de tablas (TableViewer) se parece mucho a un visor de listas, pero aporta capacidad para ver múltiples columnas de información para cada elemento de la tabla. Los visores de tablas amplían de manera significativa la función del widget de tabla de SWT al introducir el concepto de edición de una casilla. Pueden utilizarse editores de casillas especiales para permitir al usuario editar una casilla de la tabla mediante un widget de cuadro combinado, diálogo o texto. El visor de tablas maneja la creación y la colocación de estos widgets cuando el usuario los necesita para efectuar tareas de edición. Para ello, se utilizan las clases CellEditor, como por ejemplo TextCellEditor y CheckboxCellEditor.
Los widgets de texto tienen una semántica común, como el comportamiento de doble pulsación, deshacer, colorear y navegar por índice o línea. Un TextViewer es un adaptador de un widget StyledText de SWT. Los visores de texto proporcionan un modelo de documento al cliente y gestionan la conversión del documento en la información de texto con estilo que el widget de texto ha proporcionado.
Los visores de texto se explican de manera más detallada en el tema Editores del entorno de trabajo.
Para entender cómo funciona un visor, debe conocer la relación que hay entre el elemento de entrada de un visor, su contenido, su selección y la información que se visualiza realmente en el widget que está manipulando.
Llamamos elemento de entrada al objeto principal que el visor está visualizando (o editando). Desde el punto de vista del visor, un elemento de entrada puede ser cualquier objeto. No presupone que el elemento de entrada haya implementado una interfaz concreta. (El motivo se explicará más adelante, cuando se hable de los proveedores de contenido).
Un visor debe poder manejar un cambio de elemento de entrada. Si se establece un elemento de entrada nuevo en un visor, este debe volver a poblar su widget de acuerdo con el nuevo elemento y desasociarse del elemento de entrada anterior. La semántica para registrarse como escuchador en un elemento de entrada y poblar el widget basándose en el elemento varía con cada tipo de visor.
Un visor de contenido es un visor que tiene un protocolo bien definido para obtener información de su elemento de entrada. Los visores de contenido utilizan dos clases ayudantes especializadas, IContentProvider y ILabelProvider, para poblar su widget y visualizar información sobre el elemento de entrada.
IContentProvider proporciona un protocolo de ciclo de vida básico para asociar un proveedor de contenido a un elemento de entrada y manejar un cambio de elemento de entrada. Se han implementado proveedores de contenido más especializados para los diferentes tipos de visores. El proveedor de contenido más común es IStructuredContentProvider, que puede proporcionar una lista dada de objetos en forma de elemento de entrada. Se utiliza en los visores de tipo lista, como las listas, las tablas o los árboles. Por lo general, el proveedor de contenido sabe cómo correlacionar el elemento de entrada con el contenido de visor esperado.
ILabelProvider incluye alguna función más. Dado el contenido de un visor (que se deriva del elemento de entrada y del proveedor de contenido), puede producir elementos específicos de la UI, como los nombres y los iconos, que se necesitan para visualizar el contenido del visor. Los proveedores de etiquetas pueden ayudar a guardar recursos de tipo icono, porque aseguran que se utiliza la misma instancia del icono para todos los tipos semejantes de un visor.
Nota: las instancias de los proveedores concretos de contenido y etiquetas no están pensadas para compartirse entre varios visores. Incluso si todos los visores utilizan el mismo tipo de proveedor de contenido o etiquetas, cada visor debe inicializarse con su propia instancia de clase de proveedor. El protocolo del ciclo de vida del proveedor está diseñado para que haya una relación biunívoca entre un proveedor y su visor.
Los elementos de entrada, los proveedores de contenido y los proveedores de etiquetas permiten a los visores ocultar la mayoría de los detalles de implementación del proceso de poblar los widgets. Los clientes de un visor tan solo tienen que preocuparse de poblar el visor con el tipo de entrada y el proveedor de contenido correctos. El proveedor de etiquetas debe saber cómo derivar la información de la UI del contenido del visor.
La flexibilidad proporcionada por los visores, proveedores de contenido y proveedores de etiquetas queda patente viendo cómo los utiliza el entorno de trabajo.
La clase WorkbenchContentProvider representa un proveedor de contenido estructurado que obtiene el contenido de un elemento de entrada a base de solicitar sus hijos. Volvemos a utilizar el concepto de adaptadores para implementar una función genérica. Cuando se pide la lista de elementos a partir de su elemento de entrada, la clase WorkbenchContentProvider obtiene una interfaz IWorkbenchAdapter para el elemento de entrada. Si se ha registrado una interfaz IWorkbenchAdapter para el elemento de entrada, el proveedor de contenido puede presuponer con seguridad que es posible consultar el elemento para obtener información sobre sus hijos. WorkbenchContentProvider también lleva a cabo el trabajo necesario para mantener su visor actualizado cuando cambia el área de trabajo.
La clase WorkbenchLabelProvider representa un proveedor de etiquetas que obtiene una interfaz IWorkbenchAdapter de un objeto para localizar su texto y su imagen. El concepto de proveedor de etiquetas resulta particularmente útil para los objetos del entorno de trabajo porque permite a un solo proveedor de etiquetas guardar en antememoria las imágenes que se utilizan habitualmente en un visor. Por ejemplo, una vez que la clase WorkbenchLabelProvider obtiene una imagen para utilizarla en un objeto IProject, puede guardar dicha imagen en antememoria y utilizarla en todos los objetos IProject que se muestran en el visor.
Al definir un adaptador común, IWorkbenchAdapter, y registrarlo para muchos de los tipos de la plataforma, hacemos que sea posible que estos tipos queden representados correctamente en muchos de los visores comunes y de las vistas del entorno de trabajo que los contienen.