Imagine que es ingenier@ de software y le han encargado la tarea de diseñar un sistema gráfico basado en ventanas para un sistema operativo. ¿Cómo lo haría?
Lo primero que hay que notar es que en este hipotético sistema gráfico el concepto central es el de ventana. Por lo tanto, la estructura de datos principal que lo implemente debe tener en cuenta este hecho. En cualquier momento, el sistema gráfico tendrá que poder acceder como mínimo a una ventana, por lo que estas se deben organizar en una estructura de datos tal que las operaciones más comunes sean lo más eficientes posible. Antes de pensar en dicha estructura de datos, consideremos qué relaciones puede haber entre las ventanas. La relación más obvia que primero se nos ocurre es la de padre e hija. Una ventana padre puede tener una o más hijas, cada una de las cuales aparecerá encuadrada en el área de trabajo de la ventana padre. El uso más claro que podemos hacer de esta relación padre/hijo es el de crear botones, menús, cuadros de texto, etc. dentro de nuestras ventanas. Estos elementos no serán más que ventanas hijas dependientes de una ventana padre, con la salvedad de que tendrán un aspecto un tanto diferente.
Para implementar esta relación podemos pensar en incorporar a cada representación de ventana de nuestro sistema operativo un puntero hacia la ventana padre y un conjunto de punteros hacia las posibles ventanas hijas que pueda tener. ¿Un conjunto de punteros? Ciertamente, nadie nos ha limitado el número de posibles ventanas hijas que podamos crear, por lo que un conjunto de punteros podría limitarnos demasiado. Obviamente se trata de una solución que no escala bien cuando el número de ventanas es grande. Una alternativa sería mantener únicamente un puntero a la primera ventana hija, y mantener el resto de ventanas hija unidas mediante punteros, que podemos llamar hermana. Nos queda en el tintero hacia dónde orientar el puntero padre de las ventanas que son padres. ¿Quién podría ser el padre de estas ventanas? Podemos convenir que hay una ventana que siempre está presente, que la crea el sistema operativo y no el usuario, que no tiene padre y que recibe el nombre de “Escritorio”. De ahí la suposición anterior de que nuestro administrador de ventanas debe mantener al menos una ventana. Este es un esbozo de lo que hemos diseñado hasta el momento:
Tenemos una ventana especial, sin padre, denominada “Escritorio”, desde la cual se tiene acceso a la primera ventana padre. El resto de ventanas padre las tenemos unidas mediante una estructura de lista enlazada usando el puntero hermana. A su vez, cada ventana padre puede apuntar a una lista enlazada de ventanas hija, que conforman el tercer nivel de la estructura.
Podemos pensar en otro tipo de relación para nuestro sistema de ventanas. Si nos paramos a pensar, no todas las ventanas son independientes entre sí. Es decir, es posible que ciertas ventanas de aplicaciones creen otras ventanas y que el usuario por ejemplo tenga que cerrar obligatoriamente estas últimas para poder interactuar con las primeras. Esto es típico en los cuadros de diálogo. Se trata de una relación de propiedad: La ventana A es propietaria de la ventana B. Una de las primeras cosas que dan para pensar sobre esta propiedad es si es transitiva o no; es decir, si A es propietaria de B y esta a su vez lo es de C, ¿qué podemos decir de A con respecto a C? Este aspecto lo discutiremos en un próximo artículo. Esta relación de propiedad la podemos representar mediante un puntero que apunte a la ventana que es propietaria de la ventana en cuestión. Podemos imponer como restricción que una ventana hija no puede tener en propiedad ningún tipo de ventana.
Así pues, nuestra jerarquía de ventanas con este último añadido queda como sigue: Una ventana especial denominada “Escritorio”, la cual puede apuntar a una lista de ventanas que llamaremos “de primer nivel”, y un segundo nivel con ventanas “hija”, relacionadas convenientemente con sus respectivas ventanas padre. Nótese que el padre de una ventana hija podría ser otra ventana hija, por lo que el número de niveles es variable. Queda por implementar una cosa: Cuando tengamos abiertas varias ventanas en nuestro sistema, tenemos que pensar en una forma de decidir qué ventanas se muestran completamente (es decir, en primer plano), qué ventanas quedan tapadas parcialmente por ventanas en primer plano (es decir, ventanas en segundo plano), y qué ventanas no se muestran excepto su representación icónica en la barra de tareas (es decir, están minimizadas). La estructura de lista enlazada nos permite implementar estos conceptos. Como vimos, las ventanas de un determinado nivel están unidas entre sí mediante el puntero hermana. Podemos suponer que el orden en el eje Z de una determinada ventana viene dado por su posición en un determinado nivel. Concretamente, la primera ventana de un nivel (la que está más “hacia la izquierda”) “taparía” una ventana que ocupara la tercera posición. Nos gustaría también disponer de ventanas que siempre estuvieran visibles, como por ejemplo la barra de tareas de nuestro sistema operativo. Este tipo de ventanas estarían situadas al principio del nivel, marcadas con un flag especial “siempre visible”, y solamente podrían ser ocultadas por otras ventanas que igualmente tengan el flag “siempre visible”. La estructura enlazada le permite al sistema operativo “mover” de sitio la representación de las ventanas de una manera eficiente.
Ya henemos pensado en la estructura básica de nuestro administrador de ventanas, que como habrá podido intuir, no es del todo ficticio y se trata del administrador de ventanas de Windows. En un próximo artículo veremos qué necesidades surgen a partir de este diseño básico y cómo podemos implementarlas. Asimismo vamos a intentar demostrar por qué usar unas estructuras de datos y no otras, según el tipo de operaciones que realicemos. Y efectivamente, estas operaciones son las que conforman el API de programación del administrador de ventanas de Windows.
naomi_13_launica@hotmail.com