Import views plugins
[deliverable/tracecompass.git] / tmf / org.lttng.scope.tmf2.views.ui / src / org / lttng / scope / tmf2 / views / ui / timeline / widgets / timegraph / layer / TimeGraphSelectionLayer.java
1 /*
2 * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 */
9
10 package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer;
11
12 import static java.util.Objects.requireNonNull;
13
14 import java.util.concurrent.FutureTask;
15 import java.util.function.Predicate;
16 import java.util.stream.Stream;
17
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.lttng.scope.tmf2.views.core.TimeRange;
20 import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
21 import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
22 import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
23 import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.VerticalPosition;
24
25 import javafx.event.EventHandler;
26 import javafx.scene.Group;
27 import javafx.scene.input.MouseButton;
28 import javafx.scene.input.MouseEvent;
29 import javafx.scene.layout.Pane;
30 import javafx.scene.paint.Color;
31 import javafx.scene.shape.Rectangle;
32 import javafx.scene.shape.StrokeLineCap;
33
34 /**
35 * Sub-control of the time graph widget to handle the selection layer, which
36 * displays the current and ongoing selections.
37 *
38 * It also sends the corresponding selection update whenever a new selection is
39 * made.
40 *
41 * @author Alexandre Montplaisir
42 */
43 public class TimeGraphSelectionLayer extends TimeGraphLayer {
44
45 /* Style settings. TODO Move to debug options? */
46 private static final double SELECTION_STROKE_WIDTH = 1;
47 private static final Color SELECTION_STROKE_COLOR = requireNonNull(Color.BLUE);
48 private static final Color SELECTION_FILL_COLOR = requireNonNull(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.4));
49
50 /**
51 * These events are to be ignored by the time graph pane, they should
52 * "bubble up" to the scrollpane to be used for panning.
53 */
54 private static final Predicate<MouseEvent> MOUSE_EVENT_IGNORED = e -> {
55 return (e.getButton() == MouseButton.SECONDARY
56 || e.getButton() == MouseButton.MIDDLE
57 || e.isControlDown());
58 };
59
60 private final Rectangle fSelectionRect = new Rectangle();
61 private final Rectangle fOngoingSelectionRect = new Rectangle();
62
63 private final SelectionContext fSelectionCtx = new SelectionContext();
64
65 /**
66 * Constructor
67 *
68 * @param widget
69 * The corresponding time graph widget
70 * @param parentGroup
71 * The group to which this layer should add its children
72 */
73 public TimeGraphSelectionLayer(TimeGraphWidget widget, Group parentGroup) {
74 super(widget, parentGroup);
75
76 final Pane timeGraphPane = getWidget().getTimeGraphPane();
77
78 fSelectionRect.setStroke(SELECTION_STROKE_COLOR);
79 fSelectionRect.setStrokeWidth(SELECTION_STROKE_WIDTH);
80 fSelectionRect.setStrokeLineCap(StrokeLineCap.ROUND);
81
82 Stream.of(fSelectionRect, fOngoingSelectionRect).forEach(rect -> {
83 rect.setMouseTransparent(true);
84 rect.setFill(SELECTION_FILL_COLOR);
85
86 /*
87 * We keep the 'x' property at 0, and we'll use 'layoutX' to set the
88 * start position of the rectangles.
89 *
90 * See https://github.com/lttng/lttng-scope/issues/25
91 */
92 rect.xProperty().bind(JfxUtils.ZERO_PROPERTY);
93 rect.yProperty().bind(JfxUtils.ZERO_PROPERTY);
94 rect.heightProperty().bind(timeGraphPane.heightProperty());
95 });
96
97 /*
98 * Note, unlike most other controls, we will not add/remove children to
99 * the target group, we will add them once then toggle their 'visible'
100 * property.
101 */
102 fSelectionRect.setVisible(true);
103 fOngoingSelectionRect.setVisible(false);
104 getParentGroup().getChildren().addAll(fSelectionRect, fOngoingSelectionRect);
105
106 timeGraphPane.addEventHandler(MouseEvent.MOUSE_PRESSED, fSelectionCtx.fMousePressedEventHandler);
107 timeGraphPane.addEventHandler(MouseEvent.MOUSE_DRAGGED, fSelectionCtx.fMouseDraggedEventHandler);
108 timeGraphPane.addEventHandler(MouseEvent.MOUSE_RELEASED, fSelectionCtx.fMouseReleasedEventHandler);
109 }
110
111 /**
112 * Get the rectangle object representing the current selection range.
113 *
114 * @return The current selection rectangle
115 */
116 public Rectangle getSelectionRectangle() {
117 return fSelectionRect;
118 }
119
120 /**
121 * Get the rectangle object representing the ongoing selection. It is
122 * displayed while the user holds the mouse down and drags to make a
123 * selection, but before the mouse is released. The "real" selection is only
124 * applied on mouse release.
125 *
126 * @return The ongoing selection rectangle
127 */
128 public Rectangle getOngoingSelectionRectangle() {
129 return fOngoingSelectionRect;
130 }
131
132 @Override
133 public void drawContents(TimeGraphTreeRender treeRender, TimeRange timeRange,
134 VerticalPosition vPos, @Nullable FutureTask<?> task) {
135 drawSelection(timeRange);
136 }
137
138 @Override
139 public void clear() {
140 /* We don't have to clear anything */
141 }
142
143 /**
144 * Draw a new "current" selection. For times where the selection is updated
145 * elsewhere in the framework.
146 *
147 * @param timeRange
148 * The time range of the new selection
149 */
150 public void drawSelection(TimeRange timeRange) {
151 double xStart = getWidget().timestampToPaneXPos(timeRange.getStart());
152 double xEnd = getWidget().timestampToPaneXPos(timeRange.getEnd());
153 double xWidth = xEnd - xStart;
154
155 fSelectionRect.setLayoutX(xStart);
156 fSelectionRect.setWidth(xWidth);
157
158 fSelectionRect.setVisible(true);
159 }
160
161 /**
162 * Class encapsulating the time range selection, related drawing and
163 * listeners.
164 */
165 private class SelectionContext {
166
167 private boolean fOngoingSelection;
168 private double fMouseOriginX;
169
170 public final EventHandler<MouseEvent> fMousePressedEventHandler = e -> {
171 if (MOUSE_EVENT_IGNORED.test(e)) {
172 return;
173 }
174 e.consume();
175
176 if (fOngoingSelection) {
177 return;
178 }
179
180 /* Remove the current selection, if there is one */
181 fSelectionRect.setVisible(false);
182
183 fMouseOriginX = e.getX();
184
185 fOngoingSelectionRect.setLayoutX(fMouseOriginX);
186 fOngoingSelectionRect.setWidth(0);
187 fOngoingSelectionRect.setVisible(true);
188
189 fOngoingSelection = true;
190 };
191
192 public final EventHandler<MouseEvent> fMouseDraggedEventHandler = e -> {
193 if (MOUSE_EVENT_IGNORED.test(e)) {
194 return;
195 }
196 e.consume();
197
198 double newX = e.getX();
199 double offsetX = newX - fMouseOriginX;
200
201 if (offsetX > 0) {
202 fOngoingSelectionRect.setLayoutX(fMouseOriginX);
203 fOngoingSelectionRect.setWidth(offsetX);
204 } else {
205 fOngoingSelectionRect.setLayoutX(newX);
206 fOngoingSelectionRect.setWidth(-offsetX);
207 }
208
209 };
210
211 public final EventHandler<MouseEvent> fMouseReleasedEventHandler = e -> {
212 if (MOUSE_EVENT_IGNORED.test(e)) {
213 return;
214 }
215 e.consume();
216
217 fOngoingSelectionRect.setVisible(false);
218
219 /* Send a time range selection signal for the currently selected time range */
220 double startX = Math.max(0, fOngoingSelectionRect.getLayoutX());
221 // FIXME Possible glitch when selecting backwards outside of the window
222 double endX = Math.min(getWidget().getTimeGraphPane().getWidth(), startX + fOngoingSelectionRect.getWidth());
223 long tsStart = getWidget().paneXPosToTimestamp(startX);
224 long tsEnd = getWidget().paneXPosToTimestamp(endX);
225
226 getWidget().getControl().updateTimeRangeSelection(TimeRange.of(tsStart, tsEnd));
227
228 fOngoingSelection = false;
229 };
230 }
231
232 }
This page took 0.035711 seconds and 5 git commands to generate.