2 * Copyright (c) 2019 Laurent CARON.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * Laurent CARON (laurent.caron at gmail dot com) - initial API and implementation (bug 542777)
14 package org.eclipse.swt.custom;
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.graphics.*;
18 import org.eclipse.swt.widgets.*;
21 * This class add the following behaviour to <code>StyledText</code> widgets:
23 * When the user clicks on the wheel, a circle with arrows appears. When the user moves the mouse,
24 * the StyledText content is scrolled (on the right or on the left for horizontal movements, up or down for vertical movements).
29 class MouseNavigator {
30 private final StyledText parent;
31 boolean navigationActivated = false;
33 private static final int CIRCLE_RADIUS = 15;
34 private static final int CENTRAL_POINT_RADIUS = 2;
35 private Point originalMouseLocation;
36 private final Listener mouseDownListener, mouseUpListener, paintListener, mouseMoveListener, focusOutListener;
37 private boolean hasHBar, hasVBar;
38 private Cursor previousCursor;
40 MouseNavigator(final StyledText styledText) {
41 if (styledText == null) {
42 SWT.error(SWT.ERROR_NULL_ARGUMENT);
44 if (styledText.isDisposed()) {
45 SWT.error(SWT.ERROR_WIDGET_DISPOSED);
49 mouseDownListener = (event) -> {
52 parent.addListener(SWT.MouseDown, mouseDownListener);
54 mouseUpListener = (event) -> {
57 parent.addListener(SWT.MouseUp, mouseUpListener);
59 paintListener = (event) -> {
62 parent.addListener(SWT.Paint, paintListener);
64 mouseMoveListener = (event) -> {
67 parent.addListener(SWT.MouseMove, mouseMoveListener);
69 focusOutListener = (event) -> {
72 parent.addListener(SWT.FocusOut, focusOutListener);
75 void onMouseDown(Event e) {
76 if ((e.button != 2) || navigationActivated) {
80 if (!parent.isVisible() || !parent.getEnabled() || parent.middleClickPressed) {
84 // Widget has no bar or bars are not enabled
87 if (!hasHBar && !hasVBar) {
91 navigationActivated = true;
92 previousCursor = parent.getCursor();
93 parent.setCursor(parent.getDisplay().getSystemCursor(SWT.CURSOR_ARROW));
94 originalMouseLocation = getMouseLocation();
98 private void initBarState() {
99 hasHBar = computeHasHorizontalBar();
100 hasVBar = computeHasVerticalBar();
103 private boolean computeHasHorizontalBar() {
104 final ScrollBar horizontalBar = parent.getHorizontalBar();
105 final boolean hasHorizontalBar = horizontalBar != null && horizontalBar.isVisible();
106 final boolean exceedHorizontalSpace = parent.computeSize(SWT.DEFAULT, SWT.DEFAULT).x > parent.getSize().x;
107 return hasHorizontalBar && exceedHorizontalSpace;
110 private boolean computeHasVerticalBar() {
111 final ScrollBar verticalBar = parent.getVerticalBar();
112 final boolean hasVerticalBar = verticalBar != null && verticalBar.isEnabled();
113 final boolean exceedVerticalSpace = parent.computeSize(SWT.DEFAULT, SWT.DEFAULT).y > parent.getSize().y;
114 return hasVerticalBar && exceedVerticalSpace;
117 private void onMouseUp(Event e) {
118 if ((computeDist() < CIRCLE_RADIUS) && (computeDist() >= 0)) {
124 public int computeDist() {
125 if (originalMouseLocation == null) {
128 final Point mouseLocation = getMouseLocation();
129 final int deltaX = originalMouseLocation.x - mouseLocation.x;
130 final int deltaY = originalMouseLocation.y - mouseLocation.y;
131 final int dist = (int) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
135 private void deactivate() {
136 parent.setCursor(previousCursor);
137 navigationActivated = false;
138 originalMouseLocation = null;
142 private void onFocusOut(Event e) {
146 private void onMouseMove(Event e) {
147 if (!navigationActivated) {
151 final Point mouseLocation = getMouseLocation();
152 final int deltaX = originalMouseLocation.x - mouseLocation.x;
153 final int deltaY = originalMouseLocation.y - mouseLocation.y;
154 final int dist = (int) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
155 if (dist < CIRCLE_RADIUS) {
159 parent.setRedraw(false);
161 final ScrollBar bar = parent.getHorizontalBar();
162 bar.setSelection((int) (bar.getSelection() - deltaX * .1));
163 fireSelectionEvent(e, bar);
167 final ScrollBar bar = parent.getVerticalBar();
168 bar.setSelection((int) (bar.getSelection() - deltaY * .1));
169 fireSelectionEvent(e, bar);
171 parent.setRedraw(true);
175 private void fireSelectionEvent(final Event e, final ScrollBar bar) {
176 final Event event = new Event();
178 event.display = parent.getDisplay();
179 event.type = SWT.Selection;
182 for (final Listener selectionListener : bar.getListeners(SWT.Selection)) {
183 selectionListener.handleEvent(event);
187 private Point getMouseLocation() {
188 final Point cursorLocation = Display.getCurrent().getCursorLocation();
189 final Point relativeCursorLocation = parent.toControl(cursorLocation);
190 return relativeCursorLocation;
193 private void onPaint(final Event e) {
194 if (!navigationActivated) {
198 final Rectangle rect = parent.getClientArea();
199 if (rect.width == 0 || rect.height == 0) {
203 gc.setAntialias(SWT.ON);
204 gc.setAdvanced(true);
206 final Color oldForegroundColor = gc.getForeground();
207 final Color oldBackgroundColor = gc.getBackground();
208 gc.setBackground(parent.getForeground());
215 gc.setForeground(oldForegroundColor);
216 gc.setBackground(oldBackgroundColor);
219 private void drawCircle() {
220 gc.setBackground(parent.getBackground());
221 gc.setForeground(parent.getForeground());
223 gc.fillOval(originalMouseLocation.x - CIRCLE_RADIUS, originalMouseLocation.y - CIRCLE_RADIUS, CIRCLE_RADIUS * 2, CIRCLE_RADIUS * 2);
224 gc.setBackground(parent.getForeground());
226 gc.drawOval(originalMouseLocation.x - CIRCLE_RADIUS, originalMouseLocation.y - CIRCLE_RADIUS, CIRCLE_RADIUS * 2, CIRCLE_RADIUS * 2);
229 private void drawCentralPoint() {
230 gc.fillOval(originalMouseLocation.x - CENTRAL_POINT_RADIUS, originalMouseLocation.y - CENTRAL_POINT_RADIUS, CENTRAL_POINT_RADIUS * 2, CENTRAL_POINT_RADIUS * 2);
233 private void drawArrows() {
236 drawHorizontalArrows();
239 drawVerticalArrows();
243 private void drawHorizontalArrows() {
244 final int[] points = new int[6];
246 points[0] = originalMouseLocation.x - 6;
247 points[1] = originalMouseLocation.y + 3;
248 points[2] = originalMouseLocation.x - 9;
249 points[3] = originalMouseLocation.y;
250 points[4] = originalMouseLocation.x - 6;
251 points[5] = originalMouseLocation.y - 3;
252 gc.drawPolyline(points);
255 points[0] = originalMouseLocation.x + 7;
256 points[1] = originalMouseLocation.y + 3;
257 points[2] = originalMouseLocation.x + 10;
258 points[3] = originalMouseLocation.y;
259 points[4] = originalMouseLocation.x + 7;
260 points[5] = originalMouseLocation.y - 3;
261 gc.drawPolyline(points);
264 private void drawVerticalArrows() {
265 final int[] points = new int[6];
267 points[0] = originalMouseLocation.x - 3;
268 points[1] = originalMouseLocation.y - 6;
269 points[2] = originalMouseLocation.x;
270 points[3] = originalMouseLocation.y - 10;
271 points[4] = originalMouseLocation.x + 3;
272 points[5] = originalMouseLocation.y - 6;
273 gc.drawPolyline(points);
276 points[0] = originalMouseLocation.x - 3;
277 points[1] = originalMouseLocation.y + 7;
278 points[2] = originalMouseLocation.x;
279 points[3] = originalMouseLocation.y + 11;
280 points[4] = originalMouseLocation.x + 3;
281 points[5] = originalMouseLocation.y + 7;
282 gc.drawPolyline(points);
287 if (parent.isDisposed()) {
290 parent.removeListener(SWT.MouseDown, mouseDownListener);
291 parent.removeListener(SWT.MouseUp, mouseUpListener);
292 parent.removeListener(SWT.Paint, paintListener);
293 parent.removeListener(SWT.MouseMove, mouseMoveListener);
294 parent.removeListener(SWT.MouseExit, focusOutListener);