Gtk2Hs tiene varios widgets que pueden ser ajustados visualmente por el usuario usando el ratón o el teclado, como los widgets rango, descritos en su sección. También hay algunos widgets que muestran una porción de un área de datos mayor, como el widget text y el widget viewport. Esta porción también puede ser ajustada por el usuario.
Una aplicación necesita ser capaz de reaccionar a los cambios que hace el usuario en el rango. Un modo de hacerlo podría ser haciendo que cada widget emitiera su propio tipo de señal cuando cambiase su ajuste. Pero puede que quisieras enlazar los ajustes de varios widgets juntos, de modo que al ajustar uno se ajustasen los otros. El ejemplo más claro de esto es la conexión de un scrollbar (barra de deslizamiento) a un panning viewport (ventana de mostrar imágenes con una subpantalla) o a un área de texto con scroll.
El objeto Adjustment
(ajuste) se puede usar para almacenar los parámetros y valores de
configuración de los widgets de rango, como los scrollbars y controles de escalado.
Como Adjustment
se deriva de GObject
y
Object
, los ajustes pueden emitir señales, que pueden ser usadas no sólo para
que tu programa reaccione a las entradas del usuario en los widgets ajustables, sino
también para propagar los valores de ajuste de un modo transparente entre widgets ajustables.
Muchos de los widgets que usan objetos Adjustment
, como ScrolledWindow
, pueden crear
sus propios objetos Adjustment
, pero tu mismo los puedes crear con:
adjustmentNew :: Double -- valor - El valor inicial del rango -> Double -- mínimo - Valor mínimo del rango -> Double -- máximo - Valor máximo del rango -> Double -- incrementoPaso - El menor de dos posibles incrementos -> Double -- incrementoPágina - El mayor de dos posibles incrementos -> Double -- tamañoPágina - El tamaño del área visible -> IO Adjustment
La función de creación necesita un valor para cada información contenida en el objeto:
valor
Es el valor inicial y debe estar contenido entre el maximo y el mínimo.
máximo
y mínimo
fijan el margen del deslizador. Pulsando en las flechas
se incrementa el valor en incrementPaso
. Pulsando en el deslizador (slider) avanza un
incrementoPágina
. El tamañoPágina
se necesita para determinar si el final
del deslizador se mantiene en el rango. Puedes conseguir y establecer todos los parámetros de un
Adjustment
por métodos específicos o usando las funciones generales set
y get
en los atributos de Adjustment
.
onValueChanged :: Adjustment -> IO () -> IO (ConnectId Adjustment)
es la señal emitida cuando el "valor" de un Adjustment
cambia, y:
onAdjChanged :: Adjustment -> IO () -> IO (ConnectId Adjustment)
es la señal emitida cuando cambian uno o más de los otros atributos (diferentes de "valor") de un Adjustment
.
Los widgets de escalado se usan para permitir al usuario la selección visual y la manipulación del valor dentro de un rango especificado usando un deslizador. Podrías querer usar un widget escalable, por ejemplo, para ajustar el nivel de ampliación en una previsualización reducida de una imagen, o para controlar el brillo de un color o para especificar el número de minutos de inactividad antes de que un salvapantallas se active.
Las siguientes funciones crean widgets de escalado vertical y horizontal respectivamente:
vScaleNew :: Adjustment -> IO VScale hScaleNew :: Adjustment -> IO Hscale
También hay dos constructores, que no utilizan el Adjustment
:
vScaleNewWithRange :: Double ->. Double -> Double -> IO VScale hScaleNewWithRange :: Double ->. Double -> Double -> IO Hscale
Los parámetros de tipo Double
se refieren a los valores máximo, mínimo y el
paso. El incremento del paso (preferiblemente en potencias de 10) es el valor en que se
mueve la escala cuando se usan las teclas de cursor (flechas).
Las escalas horizontal y vertical son instancias de ScaleClass
y
su comportamiento común está definido en el módulo
Graphics.UI.Gtk.Abstract.Scale
.
Los widgets de escalado pueden mostrar su valor actual como un número junto al canal. (El área donde se mueve el deslizador) El comportamiento por defecto es mostrar el valor, pero puedes modificarlo con la función:
scaleSetDrawValue :: ScaleClass self => self -> Bool -> IO ()
El valor mostrado por un widget de escalado se redondea en un punto decimal por defecto, igual que el ajuste del campo valor. Esto se puede cambiar con:
scaleSetDigits :: ScaleClass self => self -> Int -> IO ()
Finalmente, el valor puede ser dibujado en diferentes posiciones relativas al canal:
scaleSetValuePos :: ScaleClass self => self -> PositionType -> IO ()
La PositionType
(tipo de posición) se define como:
data PositionType = PosLeft | PosRight | PosTop | PosBottom
Scale
hereda diferentes métodos de su clase base que es: Range
.
La política de actualizaciones de un widget rango define los puntos a los que, durante
la interacción con el usuario, cambiará el campo valor de su
Adjustment
y emitirá la señal onRangeValueChanged
de su Adjustment
. La política de actualizaciones esta definida por el
UpdateType
, que tiene tres constructores:
UpdateContinuous
(Actualización continua )onRangeValueChanged
se emite continuamente,
p.ej., cuando el deslizador se mueve un valor mínimo.
UpdateDiscontinuous
(Actualización discontinua)onRangeValueChanged
sólo se emite cuando el deslizador se ha detenido
y el usuario ha levantado el botón del ratón.
UpdateDelayed
(Actualización retrasada)onRangeValueChanged
se emite cuando el usuario levanta el botón del
ratón o si el deslizador se para de mover durante un corto periodo de tiempo.
La política de actualizaciones de un widget range se puede establecer por:
rangeSetUpdatePolicy :: RangeClass self => self -> UpdateType -> IO ()
La obtención y el ajuste de un widget rango se hace sobre la marcha con:
rangeGetAdjustment :: RangeClass self => self -> IO Adjustment rangeSetAdjustment :: RangeClass self => self -> Adjustment -> IO ()
rangeSetAdjustment
no hace nada si le pasas el objeto de Adjustment
que ya está usando,
independientemente de que alguno de los valores de sus atributos haya cambiado.
Si le pasas un nuevo código de Adjustment
, desconectará el antiguo, si existía
(posiblemente detruyéndolo), conectará las señales apropiadas al nuevo, y llamará a la función
privada gtk_range_adjustment_changed()
, que (o al menos se supone que...) recalculará
el tamaño y/o posición del deslizador y lo dibujará si fuera necesario.
Como se ha mencionado en la sección de ajustes, si quieres volver a usar el mismo Adjustment
,
cuando modificas sus valores directamente, deberías emitir la señal changed
(cambiado) sobre él.
Todos los widgets de rango de Gtk2Hs reaccionan a las pulsaciones de ratón más
o menos de la misma manera. Pulsando del botón 1 del ratón en el canal originará
que el ajuste de incrementoPaso
se añada o sustraiga de su valor,
y el deslizador se mueva de acuerdo a ello. Pulsando el botón 2 del ratón en el canal
moverá el deslizador al punto en que se pulsó el botón. Pulsando el tercer botón en el
canal o en las flechas del scroll causará que el valor del Adjustment
cambie en el valor de incrementoPaso
.
Nota: Esto no funciona en Linux Fedora 6 con la configuración estándar del ratón.
No se puede hacer que las barras de scroll tengan el foco, así que no disponen de atajos de teclado. Para los otros widgets de rango, (que sólo están activos cuando el widget tiene el foco) no diferencian entre widgets de rango vertical u horizontal.
Todos los widgets de rango se pueden usar con las flechas de izquierda, derecha, arriba y abajo,
así como con las teclas AvPág y RePág. Las flechas mueven el
deslizador arriba y abajo en incrementoPaso
, mientras AvPág y
RePág lo mueven en incrementoPágina
. Inicio y Fin
lo mueven al principio y al final del canal.
Este ejemplo crea una ventana con tres widgets de rango conectados al mismo objeto Adjustment
,
y un par de controles para ajustar algunos de los parámetros mencionados antes, de modo
que puedas ver como afecta al modo en que dichos widgets trabajan.
Las tres escalas se sitúan de modo que la barra vertical está junto a las dos
horizontales, una sobre la otra. Así que necesitamos una caja horizontal para la escala vertical
y una caja vertical junto a ella para las escalas horizontales. Las escalas y las cajas
deben empaquetarse con PackGrow
de modo que las escalas actualicen su tamaño con el de
la caja principal, que es una caja vertical en la ventana.
Todas las escalas se construyen con el mismo Adjustment
, estableciendo el valor inicial en 0.0,
el valor mínimo en 0.0, el máximo en 101.0, el incremento del paso en 0.1, el incremento de
página en 1.0 y el tamaño de la página en 1.0.
adj1 <- adjustmentNew 0.0 0.0 101.0 0.1 1.0 1.0
El usuario puede controlar cuando se muestran los valores de escalado con un
checkButton
. Esto se empaqueta en la caja principal y se establece
para estar activo inicialmente. Un botón check es un botón toggle, así que cuando
el usuario lo marca o lo desmarca, se envía la señal onToggled
.
Esto origina que la función toggleDisplay
se evalúe. La función toggleDisplay
queda definida así:
toggleDisplay :: ScaleClass self => CheckButton -> [self] -> IO () toggleDisplay b scls = sequence_ (map change scls) where change sc = do st <- toggleButtonGetActive b scaleSetDrawValue sc st
La función tiene un parámetro tipo checkButton
, y una lista de instancias
de ScaleClass
. Sin embargo, una lista sólo puede contener valores del mismo tipo,
y vScale
y hScale
son tipos diferentes. Por lo tanto, usamos la función
en listas de escalas horizontales o verticales, pero si la lista contuviera los dos tipos se originaría un
error de tipado.
El usuario puede elegir el positionType
mediante un widget del que todavía no hemos hablado,
un ComboBox
. Esto permite una selección de elecciones como se muestra más abajo. El primero en
establecerse como activo se determina por un índice, 0 aquí (el primero).
makeOpt1 :: IO ComboBox makeOpt1 = do cb <- comboBoxNewText comboBoxAppendText cb "TOP" comboBoxAppendText cb "BOTTOM" comboBoxAppendText cb "LEFT" comboBoxAppendText cb "RIGHT" comboBoxSetActive cb 0 return cb
Un segundo comboBox
permite que el usuario seleccione la política de actualizaciones,
alguno de los tres constructores de UpdateType
.
makeOpt2 :: IO ComboBox makeOpt2 = do cb <- comboBoxNewText comboBoxAppendText cb "Continuous" comboBoxAppendText cb "Discontinuous" comboBoxAppendText cb "Delayed" comboBoxSetActive cb 0 return cb
Las cajas combo (Combo box) por si mismas sólo muestran texto. Para seleccionar la posición, definimos:
setScalePos :: ScaleClass self => ComboBox -> self -> IO () setScalePos cb sc = do ntxt <- comboBoxGetActiveText cb let pos = case ntxt of (Just "TOP") -> PosTop (Just "BOTTOM") -> PosBottom (Just "LEFT") -> PosLeft (Just "RIGHT") -> PosRight Nothing -> error "setScalePos: no position set" scaleSetValuePos sc pos setUpdatePol :: RangeClass self => ComboBox -> self -> IO () setUpdatePol cb sc = do ntxt <- comboBoxGetActiveText cb let pol = case ntxt of (Just "Continuous") -> UpdateContinuous (Just "Discontinuous") -> UpdateDiscontinuous (Just "Delayed") -> UpdateDelayed Nothing -> error "setUpdatePol: no policy set" rangeSetUpdatePolicy sc pol
Aquí no hemos usado listas para gestionar las escalas verticales y horizontales, así que accedemos por separado a cada escala horizontal.
El número de precisión mostrado por las tres escalas debe ser manejado con otra escala,
para la que usamos un nuevo objeto Adjustment
. La precisión máxima es 5 y cada incremento es 1.
La precisión de la propia escala de control se establece a 1.
adj2 <- adjustmentNew 1.0 0.0 5.0 1.0 1.0 0.0
Cuando cambia el Adjustment
de control, se emite la señal onValueChanged
y se
evalúa la función setDigits
.
setDigits :: ScaleClass self => self -> Adjustment -> IO () setDigits sc adj = do val <- get adj adjustmentValue set sc [scaleDigits := (round val)]
Aquí usamos las funciones generales set
y get
sobre los
atributos; podríamos haber usado funciones específicas también. fíjate en que el
valor de tipo Double
del valor de ajuste, se debe redondear a un valor de
tipo Integral
.
Usamos otra escala horizontal para gestionar el tamaño de página de las tres
escalas del ejemplo. Cuando lo ponemos a 0.0, las escalas pueden alcanzar su
máximo inicial de 100.0 y cuando lo ponemos a 100.0 las escalas quedan fijas
en su menor valor. Esto implica el ajuste del
adjustment
por una señal de onValueChanged
desde un tercer
adjustment
mediante este trozo de código:
onValueChanged adj3 $ do val <- adjustmentGetValue adj3 adjustmentSetPageSize adj1 val
La función main es:
import Graphics.UI.Gtk main :: IO () main = do initGUI window <- windowNew set window [windowTitle := "range controls", windowDefaultWidth := 250] mainbox <- vBoxNew False 10 containerAdd window mainbox containerSetBorderWidth mainbox 10 box1 <- hBoxNew False 0 boxPackStart mainbox box1 PackGrow 0 adj1 <- adjustmentNew 0.0 0.0 101.0 0.1 1.0 1.0 vsc <- vScaleNew adj1 boxPackStart box1 vsc PackGrow 0 box2 <- vBoxNew False 0 boxPackStart box1 box2 PackGrow 0 hsc1 <- hScaleNew adj1 boxPackStart box2 hsc1 PackGrow 0 hsc2 <- hScaleNew adj1 boxPackStart box2 hsc2 PackGrow 0 chb <- checkButtonNewWithLabel "Display Value on Scale Widgets" boxPackStart mainbox chb PackNatural 10 toggleButtonSetActive chb True box3 <- hBoxNew False 10 boxPackStart mainbox box3 PackNatural 0 label1 <- labelNew (Just "Scale Value Position:") boxPackStart box3 label1 PackNatural 0 opt1 <- makeOpt1 boxPackStart box3 opt1 PackNatural 0 box4 <- hBoxNew False 10 boxPackStart mainbox box4 PackNatural 0 label2 <- labelNew (Just "Scale Update Policy:") boxPackStart box4 label2 PackNatural 0 opt2 <- makeOpt2 boxPackStart box4 opt2 PackNatural 0 adj2 <- adjustmentNew 1.0 0.0 5.0 1.0 1.0 0.0 box5 <- hBoxNew False 0 containerSetBorderWidth box5 10 boxPackStart mainbox box5 PackGrow 0 label3 <- labelNew (Just "Scale Digits:") boxPackStart box5 label3 PackNatural 10 dsc <- hScaleNew adj2 boxPackStart box5 dsc PackGrow 0 scaleSetDigits dsc 0 adj3 <- adjustmentNew 1.0 1.0 101.0 1.0 1.0 0.0 box6 <- hBoxNew False 0 containerSetBorderWidth box6 10 boxPackStart mainbox box6 PackGrow 0 label4 <- labelNew (Just "Scrollbar Page Size:") boxPackStart box6 label4 PackNatural 10 psc <- hScaleNew adj3 boxPackStart box6 psc PackGrow 0 scaleSetDigits psc 0 onToggled chb $ do toggleDisplay chb [hsc1,hsc2] toggleDisplay chb [vsc] onChanged opt1 $ do setScalePos opt1 hsc1 setScalePos opt1 hsc2 setScalePos opt1 vsc onChanged opt2 $ do setUpdatePol opt2 hsc1 setUpdatePol opt2 hsc2 setUpdatePol opt2 vsc onValueChanged adj2 $ do setDigits hsc1 adj2 setDigits hsc2 adj2 setDigits vsc adj2 onValueChanged adj3 $ do val <- adjustmentGetValue adj3 adjustmentSetPageSize adj1 val widgetShowAll window onDestroy window mainQuit mainGUI
Las funciones no estándar usadas en el listado ya han sido listadas antes.