Bug 279736 - ScrolledComponent.showControl() broken when scrollable view taller than 16335 pixels
Summary: ScrolledComponent.showControl() broken when scrollable view taller than 16335...
Status: RESOLVED WORKSFORME
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 4.0   Edit
Hardware: PC Linux-GTK
: P3 normal with 1 vote (vote)
Target Milestone: ---   Edit
Assignee: Bogdan Gheorghe CLA
QA Contact:
URL:
Whiteboard:
Keywords: triaged
Depends on:
Blocks:
 
Reported: 2009-06-10 00:00 EDT by Dave CLA
Modified: 2018-05-15 16:19 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Dave CLA 2009-06-10 00:00:40 EDT
Build ID: M20080911-1700

Steps To Reproduce:
1. Create a ScrolledComponent with content that would extend vertically more than 16335 pixels
2. Invoke showControl() on a control offset more than 16335 pixels from the top.
3. View will be scrolled incorrectly.

(It is possible the same problem exists if the horizontal width is greater than 16335, but haven't tested).


More information:
The root cause is that OS.gdk_window_get_origin() will max out at 16335 for origin_y (that is, when real origin y is >= 16335, method always returns 16335).

This method is called from Display.map(), called from showControl().

A fix that works for me is to replace the following lines in showControl():

	Rectangle itemRect = getDisplay().map(control.getParent(), this, control.getBounds());
	Rectangle area = getClientArea();
	Point origin = getOrigin();
	if (itemRect.x < 0) origin.x = Math.max(0, origin.x + itemRect.x);
	if (itemRect.y < 0) origin.y = Math.max(0, origin.y + itemRect.y);
	if (area.width < itemRect.x + itemRect.width) origin.x = Math.max(0, origin.x + itemRect.x + itemRect.width - area.width);
	if (area.height < itemRect.y + itemRect.height) origin.y = Math.max(0, origin.y + itemRect.y + itemRect.height - area.height);

With:
        Rectangle itemRect = control.getBounds();
        Rectangle area = getClientArea();
        Point origin = getOrigin();
        if (itemRect.x < origin.x) origin.x = itemRect.x;
        if (itemRect.y < origin.y) origin.y = itemRect.y;
        if (origin.x + area.width < itemRect.x + itemRect.width) origin.x = Math.max(0, itemRect.x + itemRect.width - area.width);
        if (origin.y + area.height < itemRect.y + itemRect.height) origin.y = Math.max(0, itemRect.y + itemRect.height - area.height);

In short, calling Display.map() seems to not be needed. Instead using the control bounds directly to compute the new origin works fine for me.

Fixing OS.gdk_window_get_origin() would be best. If not, fixing ScrolledControl() would be the next option. Or maybe both, since Display.map() would be slower.
Comment 1 Dave CLA 2009-06-10 01:09:11 EDT
Just realized that the solution that worked for me will only work for components, which parent is the scrollable component directly, but not for deeply nested ones.
Comment 2 Felipe Heidrich CLA 2009-06-11 17:39:15 EDT
Display.map() is necessary.
It just worked for you without because 'control.getParent()' and 'this' have the same origin.

this limit of 16335 pixels sounds suspicious. Can you give us a snippet that shows the problem ?

Bog, we can work on this together, find me if you need it.
Comment 3 Dave CLA 2009-06-11 23:50:57 EDT
I have created this example by cutting all of the unrelated code out from my actual code. Once you open the view, shrink your application window until the view is only two tiles wide (this will make the view tall enough for the test). As you click on a tile, it will be showControl()'ed. The tiles on the top will show correctly. Scroll down to about the middle of the view and click on a tile -- showControl() won't work correctly anymore.

If you cannot reproduce the problem, the issue might be with my GTK+ libraries.

public class MyView extends ViewPart {

  @Override public void createPartControl(final Composite parent) {
    final ScrolledComposite scroller = new ScrolledComposite(parent, V_SCROLL | BORDER);
    scroller.setExpandVertical(true);
    scroller.setExpandHorizontal(true);

    final Composite frame = new Composite(scroller, NONE);
    scroller.setContent(frame);
    RowLayout layout = new RowLayout();
    frame.setLayout(layout);
    
    scroller.addControlListener(new ControlAdapter() {
      // needed for row layout wrapping
      @Override public void controlResized(ControlEvent e) {
        Rectangle clientArea = scroller.getClientArea();
        scroller.setMinSize(frame.computeSize(clientArea.width, DEFAULT));
      }
    });
    
    final Image image = new Image(parent.getDisplay(), new Rectangle(0, 0, 160, 160));
    Rectangle bounds = image.getBounds();
    for (int i = 0; i < 1000; ++i) {
      final Canvas canvas = new Canvas(frame, NONE);
      canvas.setLayoutData(new RowData(bounds.width, bounds.height));
      canvas.addPaintListener(new PaintListener() {
        @Override public void paintControl(PaintEvent event) {
          event.gc.drawImage(image, 0, 0);
        }
      });
      canvas.addMouseListener(new MouseAdapter() {
        @Override public void mouseUp(MouseEvent event) {
          scroller.showControl(canvas);
        }
      });
    }
    frame.pack();
  }

  @Override public void setFocus() {
    // TODO Auto-generated method stub
  }
  
}
Comment 4 Eric Williams CLA 2018-05-15 16:19:57 EDT
Please re-open this issue if the bug is reproducible in a snippet on GTK3.22 and 4.8.