Mercurial > projects > dwt-addons
annotate dwtx/jface/internal/text/StickyHoverManager.d @ 162:1a5b8f8129df
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 08 Sep 2008 00:51:37 +0200 |
parents | 5cf141e43417 |
children |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 2007, 2008 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.internal.text.StickyHoverManager; | |
14 | |
131 | 15 import dwtx.jface.internal.text.NonDeletingPositionUpdater; // packageimport |
16 import dwtx.jface.internal.text.InternalAccessor; // packageimport | |
17 import dwtx.jface.internal.text.InformationControlReplacer; // packageimport | |
18 import dwtx.jface.internal.text.TableOwnerDrawSupport; // packageimport | |
19 import dwtx.jface.internal.text.DelayedInputChangeListener; // packageimport | |
20 | |
129 | 21 import dwt.dwthelper.utils; |
162 | 22 import tango.text.convert.Format; |
129 | 23 |
24 import dwt.DWT; | |
25 import dwt.events.ControlEvent; | |
26 import dwt.events.ControlListener; | |
27 import dwt.events.FocusEvent; | |
28 import dwt.events.FocusListener; | |
29 import dwt.events.KeyEvent; | |
30 import dwt.events.KeyListener; | |
31 import dwt.events.MouseEvent; | |
32 import dwt.events.MouseListener; | |
33 import dwt.graphics.Point; | |
34 import dwt.graphics.Rectangle; | |
35 import dwt.widgets.Control; | |
36 import dwt.widgets.Display; | |
37 import dwt.widgets.Event; | |
38 import dwt.widgets.Listener; | |
39 import dwtx.jface.text.IInformationControl; | |
40 import dwtx.jface.text.IInformationControlExtension3; | |
41 import dwtx.jface.text.IInformationControlExtension5; | |
42 import dwtx.jface.text.IViewportListener; | |
43 import dwtx.jface.text.IWidgetTokenKeeper; | |
44 import dwtx.jface.text.IWidgetTokenKeeperExtension; | |
45 import dwtx.jface.text.IWidgetTokenOwner; | |
46 import dwtx.jface.text.TextViewer; | |
47 import dwtx.jface.util.Geometry; | |
48 | |
49 /** | |
50 * Implements a sticky hover control, i.e. a control that replaces a hover | |
51 * with an enriched and focusable control. | |
52 * <p> | |
53 * The information control is made visible on request by calling | |
54 * {@link #showInformationControl(Rectangle)}. | |
55 * </p> | |
56 * <p> | |
57 * Clients usually instantiate and configure this class before using it. The configuration | |
58 * must be consistent: This means the used {@link dwtx.jface.text.IInformationControlCreator} | |
59 * must create an information control expecting information in the same format the configured | |
60 * {@link dwtx.jface.text.information.IInformationProvider}s use to encode the information they provide. | |
61 * </p> | |
62 * | |
63 * @since 3.4 | |
64 */ | |
65 public class StickyHoverManager : InformationControlReplacer , IWidgetTokenKeeper, IWidgetTokenKeeperExtension { | |
66 | |
67 /** | |
68 * Priority of the info controls managed by this sticky hover manager. | |
69 * <p> | |
70 * Note: Only applicable when info control does not have focus. | |
71 * -5 as value has been chosen in order to be beaten by the hovers of TextViewerHoverManager. | |
72 * </p> | |
73 */ | |
147 | 74 private static const int WIDGET_PRIORITY= -5; |
129 | 75 |
76 | |
77 /** | |
78 * Internal information control closer. Listens to several events issued by its subject control | |
79 * and closes the information control when necessary. | |
80 */ | |
81 class Closer : IInformationControlCloser, ControlListener, MouseListener, IViewportListener, KeyListener, FocusListener, Listener { | |
82 //TODO: Catch 'Esc' key in fInformationControlToClose: Don't dispose, just hideInformationControl(). | |
83 // This would allow to reuse the information control also when the user explicitly closes it. | |
150 | 84 |
129 | 85 //TODO: if subject control is a Scrollable, should add selection listeners to both scroll bars |
86 // (and remove the ViewPortListener, which only listens to vertical scrolling) | |
87 | |
88 /** The subject control. */ | |
89 private Control fSubjectControl; | |
90 /** Indicates whether this closer is active. */ | |
91 private bool fIsActive= false; | |
92 /** The display. */ | |
93 private Display fDisplay; | |
94 | |
95 /* | |
96 * @see IInformationControlCloser#setSubjectControl(Control) | |
97 */ | |
98 public void setSubjectControl(Control control) { | |
99 fSubjectControl= control; | |
100 } | |
101 | |
102 /* | |
103 * @see IInformationControlCloser#setInformationControl(IInformationControl) | |
104 */ | |
105 public void setInformationControl(IInformationControl control) { | |
106 // NOTE: we use getCurrentInformationControl2() from the outer class | |
107 } | |
108 | |
109 /* | |
110 * @see IInformationControlCloser#start(Rectangle) | |
111 */ | |
112 public void start(Rectangle informationArea) { | |
113 | |
114 if (fIsActive) | |
115 return; | |
116 fIsActive= true; | |
117 | |
118 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) { | |
119 fSubjectControl.addControlListener(this); | |
120 fSubjectControl.addMouseListener(this); | |
121 fSubjectControl.addKeyListener(this); | |
122 } | |
123 | |
124 fTextViewer.addViewportListener(this); | |
150 | 125 |
129 | 126 IInformationControl fInformationControlToClose= getCurrentInformationControl2(); |
127 if (fInformationControlToClose !is null) | |
128 fInformationControlToClose.addFocusListener(this); | |
129 | |
130 fDisplay= fSubjectControl.getDisplay(); | |
131 if (!fDisplay.isDisposed()) { | |
132 fDisplay.addFilter(DWT.MouseMove, this); | |
133 fDisplay.addFilter(DWT.FocusOut, this); | |
134 } | |
135 } | |
136 | |
137 /* | |
138 * @see IInformationControlCloser#stop() | |
139 */ | |
140 public void stop() { | |
141 | |
142 if (!fIsActive) | |
143 return; | |
144 fIsActive= false; | |
145 | |
146 fTextViewer.removeViewportListener(this); | |
147 | |
148 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) { | |
149 fSubjectControl.removeControlListener(this); | |
150 fSubjectControl.removeMouseListener(this); | |
151 fSubjectControl.removeKeyListener(this); | |
152 } | |
150 | 153 |
129 | 154 IInformationControl fInformationControlToClose= getCurrentInformationControl2(); |
155 if (fInformationControlToClose !is null) | |
156 fInformationControlToClose.removeFocusListener(this); | |
150 | 157 |
129 | 158 if (fDisplay !is null && !fDisplay.isDisposed()) { |
159 fDisplay.removeFilter(DWT.MouseMove, this); | |
160 fDisplay.removeFilter(DWT.FocusOut, this); | |
161 } | |
162 | |
163 fDisplay= null; | |
164 } | |
165 | |
166 /* | |
167 * @see ControlListener#controlResized(ControlEvent) | |
168 */ | |
169 public void controlResized(ControlEvent e) { | |
170 hideInformationControl(); | |
171 } | |
172 | |
173 /* | |
174 * @see ControlListener#controlMoved(ControlEvent) | |
175 */ | |
176 public void controlMoved(ControlEvent e) { | |
177 hideInformationControl(); | |
178 } | |
179 | |
180 /* | |
181 * @see MouseListener#mouseDown(MouseEvent) | |
182 */ | |
183 public void mouseDown(MouseEvent e) { | |
184 hideInformationControl(); | |
185 } | |
186 | |
187 /* | |
188 * @see MouseListener#mouseUp(MouseEvent) | |
189 */ | |
190 public void mouseUp(MouseEvent e) { | |
191 } | |
192 | |
193 /* | |
194 * @see MouseListener#mouseDoubleClick(MouseEvent) | |
195 */ | |
196 public void mouseDoubleClick(MouseEvent e) { | |
197 hideInformationControl(); | |
198 } | |
199 | |
200 /* | |
201 * @see IViewportListenerListener#viewportChanged(int) | |
202 */ | |
203 public void viewportChanged(int topIndex) { | |
204 hideInformationControl(); | |
205 } | |
206 | |
207 /* | |
208 * @see KeyListener#keyPressed(KeyEvent) | |
209 */ | |
210 public void keyPressed(KeyEvent e) { | |
211 hideInformationControl(); | |
212 } | |
213 | |
214 /* | |
215 * @see KeyListener#keyReleased(KeyEvent) | |
216 */ | |
217 public void keyReleased(KeyEvent e) { | |
218 } | |
150 | 219 |
129 | 220 /* |
221 * @see dwt.events.FocusListener#focusGained(dwt.events.FocusEvent) | |
222 */ | |
223 public void focusGained(FocusEvent e) { | |
224 } | |
150 | 225 |
129 | 226 /* |
227 * @see dwt.events.FocusListener#focusLost(dwt.events.FocusEvent) | |
228 */ | |
229 public void focusLost(FocusEvent e) { | |
162 | 230 if (DEBUG) System.out_.println(Format("StickyHoverManager.Closer.focusLost(): {}", e)); //$NON-NLS-1$ |
129 | 231 Display d= fSubjectControl.getDisplay(); |
135 | 232 d.asyncExec(new class() Runnable { |
129 | 233 // Without the asyncExec, mouse clicks to the workbench window are swallowed. |
234 public void run() { | |
235 hideInformationControl(); | |
236 } | |
237 }); | |
238 } | |
150 | 239 |
129 | 240 /* |
241 * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event) | |
242 */ | |
243 public void handleEvent(Event event) { | |
244 if (event.type is DWT.MouseMove) { | |
149 | 245 if (!( cast(Control)event.widget ) || event.widget.isDisposed()) |
129 | 246 return; |
150 | 247 |
129 | 248 IInformationControl infoControl= getCurrentInformationControl2(); |
143 | 249 if (infoControl !is null && !infoControl.isFocusControl() && cast(IInformationControlExtension3)infoControl ) { |
150 | 250 // if (DEBUG) System.out_.println("StickyHoverManager.Closer.handleEvent(): activeShell= " + fDisplay.getActiveShell()); //$NON-NLS-1$ |
134 | 251 IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) infoControl; |
129 | 252 Rectangle controlBounds= iControl3.getBounds(); |
253 if (controlBounds !is null) { | |
134 | 254 Point mouseLoc= event.display.map(cast(Control) event.widget, null, event.x, event.y); |
129 | 255 int margin= getKeepUpMargin(); |
256 Geometry.expand(controlBounds, margin, margin, margin, margin); | |
257 if (!controlBounds.contains(mouseLoc)) { | |
258 hideInformationControl(); | |
259 } | |
260 } | |
150 | 261 |
129 | 262 } else { |
263 /* | |
264 * TODO: need better understanding of why/if this is needed. | |
265 * Looks like the same panic code we have in dwtx.jface.text.AbstractHoverInformationControlManager.Closer.handleMouseMove(Event) | |
266 */ | |
267 if (fDisplay !is null && !fDisplay.isDisposed()) | |
268 fDisplay.removeFilter(DWT.MouseMove, this); | |
269 } | |
150 | 270 |
129 | 271 } else if (event.type is DWT.FocusOut) { |
162 | 272 if (DEBUG) System.out_.println(Format("StickyHoverManager.Closer.handleEvent(): focusOut: {}", event)); //$NON-NLS-1$ |
129 | 273 IInformationControl iControl= getCurrentInformationControl2(); |
274 if (iControl !is null && ! iControl.isFocusControl()) | |
275 hideInformationControl(); | |
276 } | |
277 } | |
278 } | |
279 | |
150 | 280 |
146 | 281 private const TextViewer fTextViewer; |
129 | 282 |
150 | 283 |
129 | 284 /** |
285 * Creates a new sticky hover manager. | |
150 | 286 * |
129 | 287 * @param textViewer the text viewer |
288 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
289 public this(TextViewer textViewer) { |
129 | 290 super(new DefaultInformationControlCreator()); |
150 | 291 |
129 | 292 fTextViewer= textViewer; |
293 setCloser(new Closer()); | |
150 | 294 |
129 | 295 install(fTextViewer.getTextWidget()); |
296 } | |
297 | |
298 /* | |
299 * @see AbstractInformationControlManager#showInformationControl(Rectangle) | |
300 */ | |
301 protected void showInformationControl(Rectangle subjectArea) { | |
302 if (fTextViewer !is null && fTextViewer.requestWidgetToken(this, WIDGET_PRIORITY)) | |
303 super.showInformationControl(subjectArea); | |
304 else | |
150 | 305 if (DEBUG) |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
306 System.out_.println("cancelled StickyHoverManager.showInformationControl(..): did not get widget token (with prio)"); //$NON-NLS-1$ |
129 | 307 } |
308 | |
309 /* | |
310 * @see AbstractInformationControlManager#hideInformationControl() | |
311 */ | |
312 public void hideInformationControl() { | |
313 try { | |
314 super.hideInformationControl(); | |
315 } finally { | |
316 if (fTextViewer !is null) | |
317 fTextViewer.releaseWidgetToken(this); | |
318 } | |
319 } | |
320 | |
321 /* | |
322 * @see AbstractInformationControlManager#handleInformationControlDisposed() | |
323 */ | |
324 protected void handleInformationControlDisposed() { | |
325 try { | |
326 super.handleInformationControlDisposed(); | |
327 } finally { | |
328 if (fTextViewer !is null) | |
329 fTextViewer.releaseWidgetToken(this); | |
330 } | |
331 } | |
332 | |
333 /* | |
334 * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner) | |
335 */ | |
336 public bool requestWidgetToken(IWidgetTokenOwner owner) { | |
337 hideInformationControl(); | |
150 | 338 if (DEBUG) |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
339 System.out_.println("StickyHoverManager gave up widget token (no prio)"); //$NON-NLS-1$ |
129 | 340 return true; |
341 } | |
342 | |
343 /* | |
344 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner, int) | |
345 */ | |
346 public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) { | |
347 if (getCurrentInformationControl2() !is null) { | |
348 if (getCurrentInformationControl2().isFocusControl()) { | |
150 | 349 if (DEBUG) |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
350 System.out_.println("StickyHoverManager kept widget token (focused)"); //$NON-NLS-1$ |
129 | 351 return false; |
352 } else if (priority > WIDGET_PRIORITY) { | |
353 hideInformationControl(); | |
150 | 354 if (DEBUG) |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
355 System.out_.println("StickyHoverManager gave up widget token (prio)"); //$NON-NLS-1$ |
129 | 356 return true; |
357 } else { | |
150 | 358 if (DEBUG) |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
359 System.out_.println("StickyHoverManager kept widget token (prio)"); //$NON-NLS-1$ |
129 | 360 return false; |
361 } | |
362 } | |
150 | 363 if (DEBUG) |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
364 System.out_.println("StickyHoverManager gave up widget token (no iControl)"); //$NON-NLS-1$ |
129 | 365 return true; |
366 } | |
367 | |
368 /* | |
369 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner) | |
370 */ | |
371 public bool setFocus(IWidgetTokenOwner owner) { | |
372 IInformationControl iControl= getCurrentInformationControl2(); | |
138 | 373 if ( cast(IInformationControlExtension5)iControl ) { |
134 | 374 IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) iControl; |
129 | 375 if (iControl5.isVisible()) { |
376 iControl.setFocus(); | |
377 return iControl.isFocusControl(); | |
378 } | |
379 return false; | |
380 } | |
381 iControl.setFocus(); | |
382 return iControl.isFocusControl(); | |
383 } | |
150 | 384 |
129 | 385 } |