Commit | Line | Data |
---|---|---|
c879c4db AM |
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.toolbar.debugopts; | |
11 | ||
12 | import static java.util.Objects.requireNonNull; | |
13 | ||
14 | import java.util.stream.Stream; | |
15 | ||
16 | import org.eclipse.jdt.annotation.Nullable; | |
17 | import org.lttng.scope.tmf2.views.core.config.ConfigOption; | |
18 | import org.lttng.scope.tmf2.views.ui.timeline.DebugOptions; | |
19 | ||
20 | import com.google.common.primitives.Doubles; | |
21 | import com.google.common.primitives.Ints; | |
22 | ||
23 | import javafx.event.ActionEvent; | |
24 | import javafx.geometry.Insets; | |
25 | import javafx.geometry.Pos; | |
26 | import javafx.scene.Node; | |
27 | import javafx.scene.control.ButtonBar.ButtonData; | |
28 | import javafx.scene.control.ButtonType; | |
29 | import javafx.scene.control.CheckBox; | |
30 | import javafx.scene.control.ColorPicker; | |
31 | import javafx.scene.control.Dialog; | |
32 | import javafx.scene.control.Label; | |
33 | import javafx.scene.control.Tab; | |
34 | import javafx.scene.control.TabPane; | |
35 | import javafx.scene.control.TextField; | |
36 | import javafx.scene.layout.HBox; | |
37 | import javafx.scene.layout.VBox; | |
38 | import javafx.scene.paint.Color; | |
39 | ||
40 | /** | |
41 | * Dialog to configure the debug options at runtime. | |
42 | * | |
43 | * @author Alexandre Montplaisir | |
44 | */ | |
45 | class DebugOptionsDialog extends Dialog<@Nullable Void> { | |
46 | ||
47 | private static final Insets PADDING = new Insets(20.0); | |
48 | private static final double SPACING = 10.0; | |
49 | ||
50 | private final DebugOptions fOpts; | |
51 | private final TabPane fTabPane; | |
52 | ||
53 | public DebugOptionsDialog(DebugOptionsButton button) { | |
54 | fOpts = button.getDebugOptions(); | |
55 | ||
56 | setTitle(Messages.debugOptionsDialogTitle); | |
57 | setHeaderText(Messages.debugOptionsDialogName); | |
58 | ||
59 | ButtonType resetToDefaultButtonType = new ButtonType(Messages.resetDefaultsButtonLabel, ButtonData.LEFT); | |
60 | getDialogPane().getButtonTypes().addAll(resetToDefaultButtonType, ButtonType.CANCEL, ButtonType.OK); | |
61 | ||
62 | fTabPane = new TabPane(getGeneralTab(), | |
63 | getLoadingOverlayTab(), | |
64 | getZoomTab(), | |
65 | getStateIntervalsTab(), | |
66 | getTooltipTab()); | |
67 | getDialogPane().setContent(fTabPane); | |
68 | ||
69 | /* | |
70 | * Restore the last-selected tab (that state is saved in the button), | |
71 | * and re-bind on the new dialog so that the property continues getting | |
72 | * updated. | |
73 | */ | |
74 | fTabPane.getSelectionModel().select(button.lastSelectedDialogProperty().get()); | |
75 | button.lastSelectedDialogProperty().bind(fTabPane.getSelectionModel().selectedIndexProperty()); | |
76 | ||
77 | /* What to do when the dialog is closed */ | |
78 | setResultConverter(dialogButton -> { | |
79 | fTabPane.getSelectionModel().getSelectedIndex(); | |
80 | ||
81 | if (dialogButton != ButtonType.OK) { | |
82 | return null; | |
83 | } | |
84 | /* | |
85 | * Set the debug options according to the current contents of the | |
86 | * dialog. | |
87 | */ | |
88 | getAllPropertySetters().forEach(PropertySetter::save); | |
89 | ||
90 | return null; | |
91 | }); | |
92 | ||
93 | /* Define how to "Reset Defaults" button works */ | |
94 | getDialogPane().lookupButton(resetToDefaultButtonType).addEventFilter(ActionEvent.ACTION, e -> { | |
95 | /* | |
96 | * This button should not close the dialog. Consuming the event here | |
97 | * will prevent the dialog from closing. | |
98 | */ | |
99 | e.consume(); | |
100 | ||
101 | getAllPropertySetters().forEach(ps -> { | |
102 | ConfigOption<?> option = ps.getOption(); | |
103 | option.resetToDefault(); | |
104 | ps.load(); | |
105 | }); | |
106 | ||
107 | }); | |
108 | ||
109 | } | |
110 | ||
111 | private Stream<PropertySetter> getAllPropertySetters() { | |
112 | return fTabPane.getTabs().stream() | |
113 | .flatMap(tab -> ((VBox) tab.getContent()).getChildren().stream()) | |
114 | .map(e -> requireNonNull((PropertySetter) e)); | |
115 | } | |
116 | ||
117 | // ------------------------------------------------------------------------ | |
118 | // Tab classes | |
119 | // ------------------------------------------------------------------------ | |
120 | ||
121 | private static class DebugOptionsDialogTab extends Tab { | |
122 | ||
123 | public DebugOptionsDialogTab(@Nullable String name, Node... contents) { | |
124 | VBox page = new VBox(contents); | |
125 | page.setPadding(PADDING); | |
126 | page.setSpacing(SPACING); | |
127 | ||
128 | setClosable(false); | |
129 | setText(name); | |
130 | setContent(page); | |
131 | } | |
132 | ||
133 | } | |
134 | ||
135 | private Tab getGeneralTab() { | |
136 | return new DebugOptionsDialogTab(Messages.tabNameGeneral, | |
137 | new CheckBoxControl(Messages.controlPaintingEnabled, fOpts.isPaintingEnabled), | |
138 | new IntegerTextField(Messages.controlEntryPadding, fOpts.entryPadding), | |
139 | new DoubleTextField(Messages.controlRenderRangePadding, fOpts.renderRangePadding), | |
140 | new IntegerTextField(Messages.controlUIUpdateDelay, fOpts.uiUpdateDelay), | |
141 | new CheckBoxControl(Messages.controlHScrollEnabled, fOpts.isScrollingListenersEnabled)); | |
142 | } | |
143 | ||
144 | private Tab getLoadingOverlayTab() { | |
145 | return new DebugOptionsDialogTab(Messages.tabNameLoadingOverlay, | |
146 | new CheckBoxControl(Messages.controlLoadingOverlayEnabled, fOpts.isLoadingOverlayEnabled), | |
147 | new ColorControl(Messages.controlLoadingOverlayColor, fOpts.loadingOverlayColor), | |
148 | new DoubleTextField(Messages.controlLoadingOverlayFullOpacity, fOpts.loadingOverlayFullOpacity), | |
149 | // new DoubleTextField(Messages.controlLoadingOverlayTransparentOpacity, fOpts.loadingOverlayTransparentOpacity), | |
150 | new DoubleTextField(Messages.controlLoadingOverlayFadeIn, fOpts.loadingOverlayFadeInDuration), | |
151 | new DoubleTextField(Messages.controlLoadingOverlayFadeOut, fOpts.loadingOverlayFadeOutDuration)); | |
152 | } | |
153 | ||
154 | private Tab getZoomTab() { | |
155 | return new DebugOptionsDialogTab(Messages.tabNameZoom, | |
156 | new IntegerTextField(Messages.controlZoomAnimationDuration + " (unused)", fOpts.zoomAnimationDuration), //$NON-NLS-1$ | |
157 | new DoubleTextField(Messages.controlZoomStep, fOpts.zoomStep), | |
158 | new CheckBoxControl(Messages.controlZoomPivotOnSelection, fOpts.zoomPivotOnSelection), | |
159 | new CheckBoxControl(Messages.controlZoomPivotOnMousePosition, fOpts.zoomPivotOnMousePosition)); | |
160 | } | |
161 | ||
162 | private Tab getStateIntervalsTab() { | |
163 | return new DebugOptionsDialogTab(Messages.tabNameIntervals, | |
164 | new DoubleTextField(Messages.controlIntervalOpacity, fOpts.stateIntervalOpacity) | |
165 | // multi-state Paint ? | |
166 | // state label Font ? | |
167 | ); | |
168 | } | |
169 | ||
170 | private Tab getTooltipTab() { | |
171 | return new DebugOptionsDialogTab(Messages.tabNameTooltips, | |
172 | // Tooltip Font picker | |
173 | new ColorControl(Messages.controlTooltipFontColor, fOpts.toolTipFontFill)); | |
174 | } | |
175 | ||
176 | // ------------------------------------------------------------------------ | |
177 | // Property-setting controls | |
178 | // ------------------------------------------------------------------------ | |
179 | ||
180 | private static interface PropertySetter { | |
181 | ConfigOption<?> getOption(); | |
182 | void load(); | |
183 | void save(); | |
184 | } | |
185 | ||
186 | private static class CheckBoxControl extends CheckBox implements PropertySetter { | |
187 | ||
188 | private final ConfigOption<Boolean> fOption; | |
189 | ||
190 | public CheckBoxControl(@Nullable String labelText, ConfigOption<Boolean> option) { | |
191 | fOption = option; | |
192 | setText(labelText); | |
193 | load(); | |
194 | } | |
195 | ||
196 | @Override | |
197 | public ConfigOption<?> getOption() { | |
198 | return fOption; | |
199 | } | |
200 | ||
201 | @Override | |
202 | public void load() { | |
203 | setSelected(fOption.get()); | |
204 | } | |
205 | ||
206 | @Override | |
207 | public void save() { | |
208 | fOption.set(isSelected()); | |
209 | } | |
210 | } | |
211 | ||
212 | private static class ColorControl extends HBox implements PropertySetter { | |
213 | ||
214 | private final ConfigOption<Color> fOption; | |
215 | private final ColorPicker fColorPicker; | |
216 | ||
217 | public ColorControl(@Nullable String labelText, ConfigOption<Color> option) { | |
218 | fOption = option; | |
219 | ||
220 | Label label = new Label(labelText + ":"); //$NON-NLS-1$ | |
221 | fColorPicker = new ColorPicker(option.get()); | |
222 | ||
223 | getChildren().addAll(label, fColorPicker); | |
224 | setAlignment(Pos.CENTER_LEFT); | |
225 | setSpacing(SPACING); | |
226 | } | |
227 | ||
228 | @Override | |
229 | public ConfigOption<?> getOption() { | |
230 | return fOption; | |
231 | } | |
232 | ||
233 | @Override | |
234 | public void load() { | |
235 | fColorPicker.setValue(fOption.get()); | |
236 | } | |
237 | ||
238 | @Override | |
239 | public void save() { | |
240 | Color color = fColorPicker.getValue(); | |
241 | if (color != null) { | |
242 | fOption.set(color); | |
243 | } | |
244 | } | |
245 | } | |
246 | ||
247 | private static abstract class TextFieldControl<T extends Number> extends HBox implements PropertySetter { | |
248 | ||
249 | private final ConfigOption<T> fOption; | |
250 | private final TextField fTextField = new TextField(); | |
251 | ||
252 | protected TextFieldControl(@Nullable String labelText, ConfigOption<T> option) { | |
253 | fOption = option; | |
254 | ||
255 | Label label = new Label(labelText + ":"); //$NON-NLS-1$ | |
256 | load(); | |
257 | ||
258 | getChildren().addAll(label, fTextField); | |
259 | setAlignment(Pos.CENTER_LEFT); | |
260 | setSpacing(SPACING); | |
261 | } | |
262 | ||
263 | @Override | |
264 | public ConfigOption<T> getOption() { | |
265 | return fOption; | |
266 | } | |
267 | ||
268 | @Override | |
269 | public void load() { | |
270 | fTextField.setText(fOption.get().toString()); | |
271 | } | |
272 | ||
273 | @Override | |
274 | public abstract void save(); | |
275 | ||
276 | protected TextField getTextField() { | |
277 | return fTextField; | |
278 | } | |
279 | } | |
280 | ||
281 | private static class IntegerTextField extends TextFieldControl<Integer> { | |
282 | ||
283 | public IntegerTextField(@Nullable String labelText, ConfigOption<Integer> option) { | |
284 | super(labelText, option); | |
285 | } | |
286 | ||
287 | @Override | |
288 | public void save() { | |
289 | String text = getTextField().getText(); | |
290 | Integer value = Ints.tryParse(text); | |
291 | if (value == null) { | |
292 | getOption().resetToDefault(); | |
293 | load(); | |
294 | } else { | |
295 | getOption().set(value); | |
296 | } | |
297 | } | |
298 | } | |
299 | ||
300 | private static class DoubleTextField extends TextFieldControl<Double> { | |
301 | ||
302 | public DoubleTextField(@Nullable String labelText, ConfigOption<Double> option) { | |
303 | super(labelText, option); | |
304 | } | |
305 | ||
306 | @Override | |
307 | public void save() { | |
308 | String text = getTextField().getText(); | |
309 | Double value = Doubles.tryParse(text); | |
310 | if (value == null) { | |
311 | getOption().resetToDefault(); | |
312 | load(); | |
313 | } else { | |
314 | getOption().set(value); | |
315 | } | |
316 | } | |
317 | } | |
318 | ||
319 | } |