/** | |
* Copyright (c) 2018 Laurent CARON. | |
* | |
* This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* Contributors: | |
* Laurent CARON (laurent.caron at gmail dot com) - initial API and implementation (bug 542777) | |
*/ | |
package org.eclipse.swt.custom; | |
import org.eclipse.swt.*; | |
import org.eclipse.swt.graphics.*; | |
import org.eclipse.swt.widgets.*; | |
/** | |
* This class add the following behaviour to <code>StyledText</code> widgets:<br/> | |
* When the user clicks on the wheel, a circle with arrows appears. When the user moves the mouse, | |
* the StyledText content is scrolled (on the right or on the left for horizontal movements, up or down for vertical movements).<br/> | |
* | |
* @since 3.110 | |
*/ | |
class MouseNavigator { | |
private final StyledText parent; | |
boolean navigationActivated = false; | |
private GC gc; | |
private static final int CIRCLE_RADIUS = 15; | |
private static final int CENTRAL_POINT_RADIUS = 2; | |
private Point originalMouseLocation; | |
private final Listener mouseDownListener, mouseUpListener, paintListener, mouseMoveListener, focusOutListener; | |
private boolean hasHBar, hasVBar; | |
MouseNavigator(final StyledText styledText) { | |
if (styledText == null) { | |
SWT.error(SWT.ERROR_NULL_ARGUMENT); | |
} | |
if (styledText.isDisposed()) { | |
SWT.error(SWT.ERROR_WIDGET_DISPOSED); | |
} | |
parent = styledText; | |
mouseDownListener = (event) -> { | |
onMouseDown(event); | |
}; | |
parent.addListener(SWT.MouseDown, mouseDownListener); | |
mouseUpListener = (event) -> { | |
onMouseUp(event); | |
}; | |
parent.addListener(SWT.MouseUp, mouseUpListener); | |
paintListener = (event) -> { | |
onPaint(event); | |
}; | |
parent.addListener(SWT.Paint, paintListener); | |
mouseMoveListener = (event) -> { | |
onMouseMove(event); | |
}; | |
parent.addListener(SWT.MouseMove, mouseMoveListener); | |
focusOutListener = (event) -> { | |
onFocusOut(event); | |
}; | |
parent.addListener(SWT.FocusOut, focusOutListener); | |
} | |
private void onMouseDown(Event e) { | |
if ((e.button != 2) || navigationActivated) { | |
return; | |
} | |
if (!parent.isVisible() || !parent.getEnabled()) { | |
return; | |
} | |
// Widget has no bar or bars are not enabled | |
initBarState(); | |
if (!hasHBar && !hasVBar) { | |
return; | |
} | |
navigationActivated = true; | |
parent.setCursor(parent.getDisplay().getSystemCursor(SWT.CURSOR_ARROW)); | |
originalMouseLocation = getMouseLocation(); | |
parent.redraw(); | |
} | |
private void initBarState() { | |
hasHBar = computeHasHorizontalBar(); | |
hasVBar = computeHasVerticalBar(); | |
} | |
private boolean computeHasHorizontalBar() { | |
final ScrollBar horizontalBar = parent.getHorizontalBar(); | |
final boolean hasHorizontalBar = horizontalBar != null && horizontalBar.isVisible(); | |
final boolean exceedHorizontalSpace = parent.computeSize(SWT.DEFAULT, SWT.DEFAULT).x > parent.getSize().x; | |
return hasHorizontalBar && exceedHorizontalSpace; | |
} | |
private boolean computeHasVerticalBar() { | |
final ScrollBar verticalBar = parent.getVerticalBar(); | |
final boolean hasVerticalBar = verticalBar != null && verticalBar.isEnabled(); | |
final boolean exceedVerticalSpace = parent.computeSize(SWT.DEFAULT, SWT.DEFAULT).y > parent.getSize().y; | |
return hasVerticalBar && exceedVerticalSpace; | |
} | |
private void onMouseUp(Event e) { | |
if ((computeDist() < CIRCLE_RADIUS) && (computeDist() >= 0)) { | |
return; | |
} | |
deactivate(); | |
} | |
public int computeDist() { | |
if (originalMouseLocation == null) { | |
return -1; | |
} | |
final Point mouseLocation = getMouseLocation(); | |
final int deltaX = originalMouseLocation.x - mouseLocation.x; | |
final int deltaY = originalMouseLocation.y - mouseLocation.y; | |
final int dist = (int) Math.sqrt(deltaX * deltaX + deltaY * deltaY); | |
return dist; | |
} | |
private void deactivate() { | |
parent.setCursor(parent.getDisplay().getSystemCursor(SWT.CURSOR_ARROW)); | |
navigationActivated = false; | |
originalMouseLocation = null; | |
parent.redraw(); | |
} | |
private void onFocusOut(Event e) { | |
deactivate(); | |
} | |
private void onMouseMove(Event e) { | |
if (!navigationActivated) { | |
return; | |
} | |
final Point mouseLocation = getMouseLocation(); | |
final int deltaX = originalMouseLocation.x - mouseLocation.x; | |
final int deltaY = originalMouseLocation.y - mouseLocation.y; | |
final int dist = (int) Math.sqrt(deltaX * deltaX + deltaY * deltaY); | |
if (dist < CIRCLE_RADIUS) { | |
return; | |
} | |
parent.setRedraw(false); | |
if (hasHBar) { | |
final ScrollBar bar = parent.getHorizontalBar(); | |
bar.setSelection((int) (bar.getSelection() - deltaX * .1)); | |
fireSelectionEvent(e, bar); | |
} | |
if (hasVBar) { | |
final ScrollBar bar = parent.getVerticalBar(); | |
bar.setSelection((int) (bar.getSelection() - deltaY * .1)); | |
fireSelectionEvent(e, bar); | |
} | |
parent.setRedraw(true); | |
parent.redraw(); | |
} | |
private void fireSelectionEvent(final Event e, final ScrollBar bar) { | |
final Event event = new Event(); | |
event.widget = bar; | |
event.display = parent.getDisplay(); | |
event.type = SWT.Selection; | |
event.time = e.time; | |
for (final Listener selectionListener : bar.getListeners(SWT.Selection)) { | |
selectionListener.handleEvent(event); | |
} | |
} | |
private Point getMouseLocation() { | |
final Point cursorLocation = Display.getCurrent().getCursorLocation(); | |
final Point relativeCursorLocation = parent.toControl(cursorLocation); | |
return relativeCursorLocation; | |
} | |
private void onPaint(final Event e) { | |
if (!navigationActivated) { | |
return; | |
} | |
final Rectangle rect = parent.getClientArea(); | |
if (rect.width == 0 || rect.height == 0) { | |
return; | |
} | |
gc = e.gc; | |
gc.setAntialias(SWT.ON); | |
gc.setAdvanced(true); | |
final Color oldForegroundColor = gc.getForeground(); | |
final Color oldBackgroundColor = gc.getBackground(); | |
gc.setBackground(parent.getForeground()); | |
drawCircle(); | |
drawCentralPoint(); | |
drawArrows(); | |
gc.setForeground(oldForegroundColor); | |
gc.setBackground(oldBackgroundColor); | |
} | |
private void drawCircle() { | |
gc.setBackground(parent.getBackground()); | |
gc.setForeground(parent.getForeground()); | |
gc.setAlpha(200); | |
gc.fillOval(originalMouseLocation.x - CIRCLE_RADIUS, originalMouseLocation.y - CIRCLE_RADIUS, CIRCLE_RADIUS * 2, CIRCLE_RADIUS * 2); | |
gc.setBackground(parent.getForeground()); | |
gc.setAlpha(255); | |
gc.drawOval(originalMouseLocation.x - CIRCLE_RADIUS, originalMouseLocation.y - CIRCLE_RADIUS, CIRCLE_RADIUS * 2, CIRCLE_RADIUS * 2); | |
} | |
private void drawCentralPoint() { | |
gc.fillOval(originalMouseLocation.x - CENTRAL_POINT_RADIUS, originalMouseLocation.y - CENTRAL_POINT_RADIUS, CENTRAL_POINT_RADIUS * 2, CENTRAL_POINT_RADIUS * 2); | |
} | |
private void drawArrows() { | |
gc.setLineWidth(2); | |
if (hasHBar) { | |
drawHorizontalArrows(); | |
} | |
if (hasVBar) { | |
drawVerticalArrows(); | |
} | |
} | |
private void drawHorizontalArrows() { | |
final int[] points = new int[6]; | |
// Left | |
points[0] = originalMouseLocation.x - 6; | |
points[1] = originalMouseLocation.y + 3; | |
points[2] = originalMouseLocation.x - 9; | |
points[3] = originalMouseLocation.y; | |
points[4] = originalMouseLocation.x - 6; | |
points[5] = originalMouseLocation.y - 3; | |
gc.drawPolyline(points); | |
// Right | |
points[0] = originalMouseLocation.x + 7; | |
points[1] = originalMouseLocation.y + 3; | |
points[2] = originalMouseLocation.x + 10; | |
points[3] = originalMouseLocation.y; | |
points[4] = originalMouseLocation.x + 7; | |
points[5] = originalMouseLocation.y - 3; | |
gc.drawPolyline(points); | |
} | |
private void drawVerticalArrows() { | |
final int[] points = new int[6]; | |
// Upper | |
points[0] = originalMouseLocation.x - 3; | |
points[1] = originalMouseLocation.y - 6; | |
points[2] = originalMouseLocation.x; | |
points[3] = originalMouseLocation.y - 10; | |
points[4] = originalMouseLocation.x + 3; | |
points[5] = originalMouseLocation.y - 6; | |
gc.drawPolyline(points); | |
// Lower | |
points[0] = originalMouseLocation.x - 3; | |
points[1] = originalMouseLocation.y + 7; | |
points[2] = originalMouseLocation.x; | |
points[3] = originalMouseLocation.y + 11; | |
points[4] = originalMouseLocation.x + 3; | |
points[5] = originalMouseLocation.y + 7; | |
gc.drawPolyline(points); | |
} | |
void dispose() { | |
if (parent.isDisposed()) { | |
return; | |
} | |
parent.removeListener(SWT.MouseDown, mouseDownListener); | |
parent.removeListener(SWT.MouseUp, mouseUpListener); | |
parent.removeListener(SWT.Paint, paintListener); | |
parent.removeListener(SWT.MouseMove, mouseMoveListener); | |
parent.removeListener(SWT.MouseExit, focusOutListener); | |
} | |
} |