2009-02-25 9 views
18

A veces nos encontramos con un compuesto SWT que se niega por completo a establecerse correctamente. A menudo nos encontramos con esto cuando hemos llamado a deshacerse de un compuesto, y luego lo reemplazamos por otro; aunque no parece estar estrictamente limitado a este caso.¿Por qué un SWT Composite a veces requiere una llamada para cambiar el tamaño() al diseño correctamente?

Cuando nos encontramos con este problema, aproximadamente el 50% del tiempo, podemos llamar al pack() y layout() en el compuesto ofensivo, y todo estará bien. Alrededor del 50% de las veces, sin embargo, tenemos que hacer esto:

Point p = c.getSize(); 
c.setSize(p.x+1, p.y+1); 
c.setSize(p); 

Hemos tenido que esto ocurra con casi todas las combinaciones de controladores de distribución y tal.

Ojalá tuviera una funda agradable, simple y reproducible, pero no es así. Espero que alguien reconozca este problema y diga: "Bueno, duh, te falta xyz ...."

Respuesta

31

Me parece que la memoria caché del diseño está desactualizada y necesita actualizarse.

Presentaciones en cachés de apoyo SWT, y por lo general caché tamaños preferidos de los controles, o lo que quieran para almacenar en caché:

public abstract class Layout { 
    protected abstract Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache); 
    protected boolean flushCache (Control control) {...} 
    protected abstract void layout (Composite composite, boolean flushCache); 
} 

soy relativamente nuevo en la programación SWT (ex programador Swing), pero se encontró situaciones similares en las que el diseño no se actualizó correctamente. Estaba suelen ser capaces de resolverlos utilizando los otros métodos de diseño que también hará que la disposición para limpiar su caché:

layout(boolean changed) 

layout(boolean changed, boolean allChildren) 

Espero que ayude ...

+1

+1 y aceptado. Ojalá pudiera +10 este: P – Jared

+0

He tenido el mismo problema que el autor de la pregunta. diseño (verdadero, cierto) me ha ayudado. – ka3ak

+0

¡Gran respuesta! +1 –

14

diseño de un compuesto es responsable de trazar los niños de ese compuesto. Entonces, si el tamaño del compuesto no cambia, pero las posiciones y tamaños relativos de los niños necesitan ser actualizados, llame al layout() en el compuesto. Sin embargo, si es necesario actualizar el tamaño o la posición del compuesto, deberá llamar al layout() en su compuesto principal (y así sucesivamente, hasta que llegue al shell).

Una regla de oro: si ha agregado o eliminado un control, o ha realizado algo que requiere una retransmisión, suba la jerarquía de widgets hasta que encuentre un compuesto con barras de desplazamiento y llame al layout(). La razón para detenerse en el compuesto con barras de desplazamiento es que su tamaño no cambiará en respuesta al cambio: sus barras de desplazamiento "absorberán" eso.

Tenga en cuenta que si el cambio que requiere un diseño no es un nuevo elemento secundario, o un elemento secundario eliminado, debe llamar al Composite.changed(new Control[] {changedControl}) antes de llamar al diseño.

+0

Brillante respuesta – Johnny000

18

Mientras tanto, aprendí un poco más sobre las deficiencias de SWT al cambiar o cambiar el tamaño de partes de la jerarquía de control en tiempo de ejecución. ScrolledComposite sy ExpandBar s también necesitan actualizarse explícitamente cuando deben adaptar sus tamaños de contenido mínimo o preferido.

escribí un pequeño método de ayuda que revalida el diseño de una jerarquía de control para un control que ha cambiado:

public static void revalidateLayout (Control control) { 

    Control c = control; 
    do { 
     if (c instanceof ExpandBar) { 
      ExpandBar expandBar = (ExpandBar) c; 
      for (ExpandItem expandItem : expandBar.getItems()) { 
       expandItem 
        .setHeight(expandItem.getControl().computeSize(expandBar.getSize().x, SWT.DEFAULT, true).y); 
      } 
     } 
     c = c.getParent(); 

    } while (c != null && c.getParent() != null && !(c instanceof ScrolledComposite)); 

    if (c instanceof ScrolledComposite) { 
     ScrolledComposite scrolledComposite = (ScrolledComposite) c; 
     if (scrolledComposite.getExpandHorizontal() || scrolledComposite.getExpandVertical()) { 
      scrolledComposite 
       .setMinSize(scrolledComposite.getContent().computeSize(SWT.DEFAULT, SWT.DEFAULT, true)); 
     } else { 
      scrolledComposite.getContent().pack(true); 
     } 
    } 
    if (c instanceof Composite) { 
     Composite composite = (Composite) c; 
     composite.layout(true, true); 
    } 
} 
+1

Sí - He tenido que hacer eso antes - es un dolor. No estaría de más formular esto como una pregunta y respuesta separada, por el bien de la búsqueda. – Jared

3

simplemente me he dado cuenta de Composite.changed (Control de los niños []).Hay un extenso artículo que leí hace un par de años:

http://www.eclipse.org/articles/article.php?file=Article-Understanding-Layouts/index.html

Este artículo también menciona que llamar Composite.layout (booleano cambió, todo booleano) para actualizar el esquema: "Llamando layout() es el lo mismo que llamar a layout (true) que le dice a ColumnLayout que descargue sus cachés antes de establecer los límites de los elementos secundarios ". Eso es todo correcto y lo que he estado haciendo desde entonces. Pero no es lo que uno quiere, ya que básicamente no tiene el beneficio del caché de diseño cuando desea actualizar el diseño porque uno o algunos controles han cambiado los requisitos.

Imagine que tiene un montón de widgets StyledText en un GridLayout y necesita cambiar el tamaño de uno de ellos. Llamar a computeSize() en StyledText es muy caro. En lugar de esto:

incorrecto:

parent.layout(true); 

... que exige computeSize() en todos los niños, a pesar de que sus necesidades no han cambiado. Usted debe hacer esto:

Derecha:

parent.changed(new Control[] { theChangedChild }); 

Entonces, o

rootComposite.layout(false, true); 

o

parent.layout(false); 

No es muy intuitiva. El parámetro para el diseño() tiene un mal nombre. En lugar de llamarlo "cambiado" debería llamarse "ignorar Cache" o algo así. Lo intuitivo es pasar "verdad" cuando algo cambió. En su lugar, debe pasar "falso", pero invalidar la caché solo para el Control cambiado con changed() antes de hacerlo ...

Tenga en cuenta que la llamada a changed() también invalidará recursivamente la memoria caché solo para el control principal en su propio padre, lo cual tiene sentido. Por lo tanto, cuando llame a layout(), debe llamarlo en el compuesto de raíz (generalmente el Shell) con all = true, a menos que sepa que el tamaño del padre del control modificado cambiará o no en respuesta.

Cuestiones relacionadas