Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 358183

Summary: SWT Combo text alignment on resize wrong
Product: [Eclipse Project] Platform Reporter: Balazs <b.lichtl>
Component: SWTAssignee: Platform-SWT-Inbox <platform-swt-inbox>
Status: CLOSED WONTFIX QA Contact:
Severity: minor    
Priority: P3 CC: hirdhird, rheydenr, r_pattanaik
Version: 3.7   
Target Milestone: ---   
Hardware: PC   
OS: Windows XP   
Whiteboard: stalebug

Description Balazs CLA 2011-09-20 02:46:51 EDT
Build Identifier: 

On resising a combo to be smaller than the width of the item text, the right side of the selected text remains visible, not the left, which is not how it was in 3.1.1 and not what is wanted by users.

The problem arose when migrating an SWT 3.1.1 application to 3.6.2. The different versions tested have the following sympthoms:
3.1.1: always the left part of the text is visible -> desired behaviour
3.4.1: at dialog open, the left part is visible, but the right side becomes visible on resizing
3.6.2 and 3.7: right side of the text is visible even at dialog open, it remains so at resize. This is the worst possible behaviour.

We could not find any setting to influence this behaviour, the only workaround I have found is in the reproduce snippet commented out, but that is not a viable solution for our app, as the SWT code is generated and there are many (around 100) combo's affected.

Reproducible: Always

Steps to Reproduce:
1) compile and run this code:
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        GridLayout layout = new GridLayout();
        shell.setLayout(layout);
        final Combo combo = new Combo(shell, SWT.DROP_DOWN | SWT.BORDER);
        GridData data = new GridData();
        data.horizontalAlignment = GridData.FILL;
        data.grabExcessHorizontalSpace = true;
        combo.setLayoutData(data);

        String item = "BbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaCc";
        combo.add(item);
        combo.setText(item);
        combo.setEnabled(false);
        //Uncomment the following lines and the text is drawn correctly
//        combo.addListener(SWT.Resize, new Listener() {
//          public void handleEvent(final Event argEvent) {
//              combo.setText(combo.getText());
//          }
//        });

        shell.pack();
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }

2) make the dialog smaller and you will see the effect described.
Comment 1 Jan Buda CLA 2012-04-12 17:32:00 EDT
I ran into this bug with SWT CCombo - exactly the same problem. The workaround from Balazs does not work, so I wrote my own. It could probably be done more efficiently, nevertheless following code works for resizing the column as well as for selecting longer item:

    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        GridLayout layout = new GridLayout();
        shell.setLayout(layout);

        Table table = new Table(shell, SWT.BORDER);

        table.setLayout(new FillLayout());
        table.setHeaderVisible(true);
        TableColumn column = new TableColumn(table, SWT.NONE);
        column.setWidth(150);
        TableItem item = new TableItem(table, SWT.NONE);
        TableEditor editor = new TableEditor(table);

        final CCombo combo = new CCombo(table, SWT.DROP_DOWN | SWT.BORDER);

        GridData data = new GridData();
        data.horizontalAlignment = GridData.FILL;
        data.grabExcessHorizontalSpace = true;
        combo.setLayoutData(data);

        String itemString = "BbbCDEFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaCc";
        String itemString2 = "hello";
        
        combo.add(itemString);
        combo.add(itemString2);
        combo.select(1);
        combo.setData("selectionIndex", combo.getSelectionIndex());

        combo.setEditable(false);
        editor.grabHorizontal = true;
        editor.setEditor(combo, item, 0);

        combo.addListener(SWT.Selection, new Listener() {

            @Override
            public void handleEvent(final Event e) {
                //We need the original text, not the displayed substring which would return methods such as combo.getText() or combo.getItem(combo.getSelectionIndex()) 
                combo.setData("selectionIndex", combo.getSelectionIndex());
                String text = combo.getItem(combo.getSelectionIndex());
                GC gc = new GC(combo);
                //Exact dimensions of selected text            
                int textWidth = gc.stringExtent(text).x;
                //reset text limit
                combo.setTextLimit(text.length());

                //In case the text is wider then the area on which it's displayed, we need to set a textLimit
                //because part of the CCombo client area is occupied by a dropdown menu arrow, we use a magic constant 
                //(Unfortunately I don't know how to find out the exact width of the combo area on which the text is displayed)
                int magicConst = 14;
                int comboWidth = combo.getClientArea().width - magicConst;
                if (textWidth > comboWidth) {
                    //find text limit - first we set it according to average char width of our text
                    int averageCharWidth = textWidth / text.length();
                    int tempLimit = comboWidth / averageCharWidth;
                    //then we fine-tune the limit - it must be as precise as possible
                    while (tempLimit > 0 && (comboWidth < gc.stringExtent(text.substring(0, tempLimit + 1)).x)) {
                        tempLimit--;
                    }
                    //textLimit must not be zero
                    if (tempLimit == 0) {
                        tempLimit++;
                    }
                    combo.setTextLimit(tempLimit);
                }
                combo.setText(text);
                gc.dispose();
            }
        });

        combo.addListener(SWT.Resize, new Listener() {

            @Override
            public void handleEvent(final Event e) {
                //We need the original text, not the displayed substring which would return methods such as combo.getText() or combo.getItem(combo.getSelectionIndex())   
                String text = combo.getItem((int) combo.getData("selectionIndex"));
                //reset text limit
                combo.setTextLimit(text.length());
                GC gc = new GC(combo);
                //exact dimensions of selected text            
                int textWidth = gc.stringExtent(text).x;
                int magicConst = 14;
                int comboWidth = combo.getClientArea().width - magicConst;
                //In case the text is wider then the area on which it's displayed, we need to set a textLimit
                if (textWidth > comboWidth) {
                    //find text limit - first we set it according to average char width of our text
                    int averageCharWidth = textWidth / text.length();
                    int tempLimit = comboWidth / averageCharWidth;
                    //sometimes on resize it can happen that computed tempLimit is greater than text length
                    if (tempLimit >= text.length()) {
                        tempLimit = text.length() - 1;
                    }
                    //then we fine-tune the limit - it must be as precise as possible    
                    while (tempLimit > 0 && (comboWidth < gc.stringExtent(text.substring(0, tempLimit + 1)).x)) {
                        tempLimit--;
                    }
                    //textLimit must not be zero
                    if (tempLimit == 0) {
                        tempLimit++;
                    }
                    combo.setTextLimit(tempLimit);
                }
                combo.setText(text);
                gc.dispose();
            }
        });

        shell.pack();
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
Comment 2 Rajiv Pattanaik CLA 2018-05-24 14:59:21 EDT
I am not sure if this would be useful to anyone at this stage, but I am adding this just in case. 

I actually implemented this to get around the bug.  Then a colleague suggested to position the cursor to the beginning of the combo box.  That seemed to work OK.


// Get a listener
public Listener getComboListener(final Combo combo) {
	Listener comboListener = new Listener() {

		@Override
		public void handleEvent(final org.eclipse.swt.widgets.Event e) 
{

				combo.setSelection(new Point(0, 0));
			}
		};
	return comboListener;
}

Then for the combo, add the lsitener for the following events

combo.addListener(SWT.Selection, getComboListener(combo));
combo.addListener(SWT.Resize, getComboListener(combo));
combo.addListener(SWT.FocusOut, getComboListener(combo));
Comment 3 Eclipse Genie CLA 2020-07-06 05:25:40 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.