/** * Copyright (c) 2019 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 StyledText widgets: *

* 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). *

* * @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; private Cursor previousCursor; 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); } void onMouseDown(Event e) { if ((e.button != 2) || navigationActivated) { return; } if (!parent.isVisible() || !parent.getEnabled() || parent.middleClickPressed) { return; } // Widget has no bar or bars are not enabled initBarState(); if (!hasHBar && !hasVBar) { return; } navigationActivated = true; previousCursor = parent.getCursor(); 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(previousCursor); 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); } }