Skip to content

Commit f479bb8

Browse files
committed
Added VScrollCtl and VScrollBubble for easier VScroll handling (#10).
1 parent 00f413c commit f479bb8

File tree

3 files changed

+175
-0
lines changed

3 files changed

+175
-0
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased]
44
### Added
55
- `CoatMux.Layer` now exposes its root `Control` instance ([#12](https://github.com/diffplug/durian-swt/issues/12)).
6+
- `VScrollCtl` and `VScrollBubble` for easier VScroll handling ([#10](https://github.com/diffplug/durian-swt/issues/10)).
67
### Removed
78
- **BREAKING** Removed `ColorPool.getSystemColor()` ([#8](https://github.com/diffplug/durian-swt/issues/8)).
89
- **BREAKING** Switched type arguments of `TypedDataField` ([#3](https://github.com/diffplug/durian-swt/issues/3)).
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (C) 2020-2021 DiffPlug, LLC - All Rights Reserved
3+
* Unauthorized copying of this file via any medium is strictly prohibited.
4+
* Proprietary and confidential.
5+
* Please send any inquiries to Ned Twigg <[email protected]>
6+
*/
7+
package com.diffplug.common.swt;
8+
9+
import java.util.Optional;
10+
11+
import org.eclipse.swt.SWT;
12+
import org.eclipse.swt.custom.ScrolledComposite;
13+
import org.eclipse.swt.graphics.Point;
14+
import org.eclipse.swt.graphics.Rectangle;
15+
import org.eclipse.swt.widgets.Composite;
16+
import org.eclipse.swt.widgets.Display;
17+
import org.eclipse.swt.widgets.Event;
18+
import org.eclipse.swt.widgets.ScrollBar;
19+
import org.eclipse.swt.widgets.Scrollable;
20+
import org.eclipse.swt.widgets.Tree;
21+
import org.eclipse.swt.widgets.TreeItem;
22+
23+
import com.diffplug.common.swt.SwtMisc;
24+
import com.diffplug.common.tree.TreeStream;
25+
26+
/**
27+
* Bubbles vertical scroll events up to a parent container, so that you don't get stuck.
28+
* Doesn't work great on mac due to rubber-banding.
29+
*/
30+
public class VScrollBubble {
31+
private static Display appliedTo;
32+
33+
/** Returns true iff the given scrollable is maxed out for scrolling in the direction of the given event. */
34+
private static boolean isMaxed(Event e, Scrollable scrollable) {
35+
ScrollBar scrollBar = scrollable.getVerticalBar();
36+
if (scrollBar == null) {
37+
return true;
38+
} else {
39+
boolean isMaxed;
40+
// System.out.println("[" + scrollBar.getMinimum() + " " + scrollBar.getMaximum() + "] " + scrollBar.getSelection() + " thumb=" + scrollBar.getThumb());
41+
if (e.count > 0) {
42+
// scrolling up
43+
isMaxed = scrollBar.getSelection() <= 0;
44+
} else {
45+
// scrolling down
46+
isMaxed = scrollBar.getSelection() >= scrollBar.getMaximum() - scrollBar.getThumb();
47+
if (!isMaxed) {
48+
// special case for trees & tables which have all their items visible, where scrolling down gives
49+
// scrollBar.getMinimum()/getMaximum=[0 100] scrollBar.getSelection()=0 scrollBar.getThumb()=10
50+
if (scrollable instanceof Tree) {
51+
Tree tree = (Tree) scrollable;
52+
int itemCount = tree.getItemCount();
53+
if (itemCount == 0) {
54+
return true;
55+
}
56+
TreeItem lastItem = tree.getItem(itemCount - 1);
57+
while (lastItem.getItemCount() > 0) {
58+
lastItem = lastItem.getItem(lastItem.getItemCount() - 1);
59+
}
60+
Rectangle itemBounds = lastItem.getBounds();
61+
Rectangle clientArea = tree.getClientArea();
62+
return itemBounds.y + itemBounds.height < clientArea.height;
63+
}
64+
}
65+
}
66+
return isMaxed;
67+
}
68+
}
69+
70+
/** Applies the necessary display filter, don't worry about calling it more than once. */
71+
public static void applyTo(Display display) {
72+
// prevent double-apply
73+
if (display == appliedTo) {
74+
return;
75+
}
76+
appliedTo = display;
77+
display.addFilter(SWT.MouseVerticalWheel, e -> {
78+
if (!(e.widget instanceof Scrollable)) {
79+
return;
80+
}
81+
// find the parent two levels up
82+
Composite parentOfScrollable = ((Scrollable) e.widget).getParent();
83+
if (parentOfScrollable == null) {
84+
return;
85+
}
86+
parentOfScrollable = parentOfScrollable.getParent();
87+
if (parentOfScrollable == null) {
88+
return;
89+
}
90+
91+
boolean isMaxed = isMaxed(e, (Scrollable) e.widget);
92+
if (!isMaxed) {
93+
return;
94+
}
95+
96+
Optional<ScrolledComposite> firstNotMaxed = TreeStream.toParent(SwtMisc.treeDefComposite(), parentOfScrollable)
97+
.filter(c -> c instanceof ScrolledComposite && SwtMisc.flagIsSet(SWT.V_SCROLL, c))
98+
.map(ScrolledComposite.class::cast)
99+
.filter(sc -> !isMaxed(e, sc))
100+
.findFirst();
101+
if (!firstNotMaxed.isPresent()) {
102+
// there isn't a parent which we can bubble the scroll to
103+
return;
104+
}
105+
ScrolledComposite toScroll = firstNotMaxed.get();
106+
Point origin = toScroll.getOrigin();
107+
origin.y -= e.count * toScroll.getVerticalBar().getIncrement();
108+
toScroll.setOrigin(origin);
109+
110+
// copy the event
111+
Event copy = SwtMisc.copyEvent(e);
112+
// cancel the old one
113+
e.doit = false;
114+
// and send the copy to the parent
115+
copy.widget = toScroll;
116+
Point d = ((Scrollable) e.widget).toDisplay(e.x, e.y);
117+
Point mapped = toScroll.toControl(d);
118+
copy.x = mapped.x;
119+
copy.y = mapped.y;
120+
copy.widget.notifyListeners(SWT.MouseVerticalWheel, copy);
121+
});
122+
}
123+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (C) 2018-2020 DiffPlug, LLC - All Rights Reserved
3+
* Unauthorized copying of this file via any medium is strictly prohibited.
4+
* Proprietary and confidential.
5+
* Please send any inquiries to Ned Twigg <[email protected]>
6+
*/
7+
package com.diffplug.common.swt.widgets;
8+
9+
import com.diffplug.common.swt.VScrollBubble;
10+
import org.eclipse.swt.SWT;
11+
import org.eclipse.swt.custom.ScrolledComposite;
12+
import org.eclipse.swt.graphics.Point;
13+
import org.eclipse.swt.widgets.Composite;
14+
import org.eclipse.swt.widgets.Control;
15+
16+
import com.diffplug.common.swt.ControlWrapper;
17+
18+
/**
19+
* SWT provides {@link ScrolledComposite} which provides a wealth of
20+
* scrolling behavior ([google](https://www.google.com/search?q=SWT+ScrolledComposite)).
21+
* However, the most common case is that you only want to scroll vertically. This class
22+
* makes that easy. Just create your content with {@link #getParentForContent()} as the
23+
* parent, and then set it to be the content with {@link #setContent(Control)}. Easy!
24+
*/
25+
public class VScrollCtl extends ControlWrapper.AroundControl<ScrolledComposite> {
26+
public VScrollCtl(Composite parent, int style) {
27+
super(new ScrolledComposite(parent, SWT.VERTICAL | style));
28+
VScrollBubble.applyTo(parent.getDisplay());
29+
}
30+
31+
public ScrolledComposite getParentForContent() {
32+
return wrapped;
33+
}
34+
35+
public void setContent(ControlWrapper wrapper) {
36+
setContent(wrapper.getRootControl());
37+
}
38+
39+
public void setContent(Control content) {
40+
wrapped.setContent(content);
41+
wrapped.setExpandHorizontal(true);
42+
wrapped.setExpandVertical(true);
43+
wrapped.addListener(SWT.Resize, e -> {
44+
int scrollWidth = wrapped.getVerticalBar().getSize().x;
45+
Point size = wrapped.getSize();
46+
Point contentSize = content.computeSize(size.x - scrollWidth, SWT.DEFAULT);
47+
content.setSize(contentSize);
48+
wrapped.setMinSize(contentSize);
49+
});
50+
}
51+
}

0 commit comments

Comments
 (0)