Mercurial > projects > dwt-addons
comparison dwtx/novocode/ishell/internal/TitleBar.d @ 188:e3780acbbf80
Added ported sources from Novocode, thanks to WasserDragoon
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 26 Oct 2008 14:54:39 +0100 |
parents | |
children | 71ca5bcf2307 |
comparison
equal
deleted
inserted
replaced
187:293a2f22f944 | 188:e3780acbbf80 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2005 Stefan Zeiger 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.novocode.com/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * Stefan Zeiger (szeiger@novocode.com) - initial API and implementation | |
10 *******************************************************************************/ | |
11 | |
12 module dwtx.novocode.ishell.internal.TitleBar; | |
13 | |
14 import dwt.dwthelper.utils; | |
15 import dwt.dwthelper.Runnable; | |
16 import dwtx.dwtxhelper.Timer; | |
17 import dwtx.dwtxhelper.TimerTask; | |
18 | |
19 import dwt.DWT; | |
20 import dwt.graphics.Color; | |
21 import dwt.graphics.Font; | |
22 import dwt.graphics.FontData; | |
23 import dwt.graphics.GC; | |
24 import dwt.graphics.Image; | |
25 import dwt.graphics.ImageData; | |
26 import dwt.graphics.PaletteData; | |
27 import dwt.graphics.Point; | |
28 import dwt.graphics.Rectangle; | |
29 import dwt.graphics.RGB; | |
30 import dwt.widgets.Canvas; | |
31 import dwt.widgets.Display; | |
32 import dwt.widgets.Event; | |
33 import dwt.widgets.Listener; | |
34 import dwt.widgets.Menu; | |
35 import dwt.widgets.MenuItem; | |
36 import dwt.widgets.Shell; | |
37 | |
38 import dwtx.novocode.ishell.DesktopForm; | |
39 import dwtx.novocode.ishell.InternalShell; | |
40 | |
41 alias char[] String; | |
42 | |
43 | |
44 /** | |
45 * A title bar for an InternalShell. | |
46 * | |
47 * @author Stefan Zeiger (szeiger@novocode.com) | |
48 * @since Jan 21, 2005 | |
49 * @version $Id: TitleBar.java 342 2005-07-09 20:37:13 +0000 (Sat, 09 Jul 2005) szeiger $ | |
50 */ | |
51 | |
52 class TitleBar : Canvas | |
53 { | |
54 private static const long UPDATE_DELAY = 25; | |
55 private static const int MINIMUM_GRAB_AREA = 2; | |
56 private static const String ELLIPSIS = "..."; | |
57 private static const int LEFT_PADDING = 2; | |
58 private static const int RIGHT_PADDING = 2; | |
59 private static const int IMAGE_SIZE = 16; | |
60 private static const int TOOL_SIZE = 14; | |
61 private static const int TOP_PADDING = 1; | |
62 private static const int BOTTOM_PADDING = 1; | |
63 | |
64 private int mouseDownOffsetX, mouseDownOffsetY, snapBackX, snapBackY; | |
65 private bool cancelled; | |
66 private /**volatile*/ long lastUpdate; | |
67 private Timer timer; | |
68 private TimerTask timerTask; | |
69 private InternalShell ishell; | |
70 private DesktopForm desktop; | |
71 private String text; | |
72 private Image image; | |
73 private bool styleClose, styleMax, styleTool, styleMin; | |
74 private Image closeImage, restoreImage, maximizeImage, minimizeImage; | |
75 private MenuItem restoreItem, closeItem, maximizeItem; | |
76 private Menu defaultPopup; | |
77 private Point minGrabSize; | |
78 private Shell shell; | |
79 private Color gradStartColor, gradEndColor, textColor, inactiveGradStartColor, inactiveGradEndColor, inactiveTextColor; | |
80 private Listener activateListener, deactivateListener; | |
81 | |
82 | |
83 this(InternalShell parent, int style) | |
84 { | |
85 super(parent, checkStyle(style)); | |
86 this.timer = new Timer(true); | |
87 this.minGrabSize = new Point(MINIMUM_GRAB_AREA, MINIMUM_GRAB_AREA); | |
88 this.ishell = parent; | |
89 this.desktop = cast(DesktopForm)ishell.getParent(); | |
90 this.styleClose = (style & DWT.CLOSE) !is 0; | |
91 this.styleMax = (style & DWT.MAX) !is 0; | |
92 this.styleMin = (style & DWT.MIN) !is 0; | |
93 this.styleTool = (style & DWT.TOOL) !is 0; | |
94 | |
95 Display display = getDisplay(); | |
96 shell = getShell(); | |
97 | |
98 gradStartColor = display.getSystemColor(DWT.COLOR_TITLE_BACKGROUND); | |
99 gradEndColor = display.getSystemColor(DWT.COLOR_TITLE_BACKGROUND_GRADIENT); | |
100 textColor = display.getSystemColor(DWT.COLOR_TITLE_FOREGROUND); | |
101 inactiveGradStartColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND); | |
102 inactiveGradEndColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT); | |
103 inactiveTextColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_FOREGROUND); | |
104 | |
105 GC gc = new GC(this); | |
106 int imgHeight = gc.getFontMetrics().getHeight()-1; | |
107 if(imgHeight%2 is 0) imgHeight--; | |
108 gc.dispose(); | |
109 | |
110 closeImage = createMenuImage(IMAGE_TYPE_CLOSE, imgHeight); | |
111 restoreImage = createMenuImage(IMAGE_TYPE_RESTORE, imgHeight); | |
112 maximizeImage = createMenuImage(IMAGE_TYPE_MAXIMIZE, imgHeight); | |
113 minimizeImage = createMenuImage(IMAGE_TYPE_MINIMIZE, imgHeight); | |
114 | |
115 setFont(createTitleFont(getFont(), styleTool)); | |
116 | |
117 defaultPopup = new Menu(this); | |
118 | |
119 restoreItem = new MenuItem(defaultPopup, DWT.PUSH); | |
120 restoreItem.setText("&Restore"); | |
121 restoreItem.setImage(restoreImage); | |
122 restoreItem.addListener(DWT.Selection, dgListener(&restoreListener)); | |
123 | |
124 MenuItem minimizeItem = new MenuItem(defaultPopup, DWT.PUSH); | |
125 minimizeItem.setText("Mi&nimize"); | |
126 minimizeItem.setEnabled(styleMin); | |
127 minimizeItem.setImage(minimizeImage); | |
128 minimizeItem.addListener(DWT.Selection, dgListener(&minimizeListener)); | |
129 | |
130 maximizeItem = new MenuItem(defaultPopup, DWT.PUSH); | |
131 maximizeItem.setText("Ma&ximize"); | |
132 maximizeItem.setImage(maximizeImage); | |
133 maximizeItem.addListener(DWT.Selection, dgListener(&maximizeListener)); | |
134 | |
135 new MenuItem(defaultPopup, DWT.SEPARATOR); | |
136 | |
137 closeItem = new MenuItem(defaultPopup, DWT.PUSH); | |
138 closeItem.setText("&Close"); | |
139 closeItem.setEnabled(styleClose); | |
140 closeItem.setImage(closeImage); | |
141 closeItem.addListener(DWT.Selection, dgListener(&closeListener)); | |
142 | |
143 addListener(DWT.Paint, dgListener(&onPaint)); | |
144 addListener(DWT.MouseDown, dgListener(&onMouseDown)); | |
145 addListener(DWT.MenuDetect, dgListener(&onMenuDetect)); | |
146 addListener(DWT.MouseDoubleClick, dgListener(&onMouseDoubleClick)); | |
147 addListener(DWT.MouseMove, dgListener(&onMouseMove)); | |
148 addListener(DWT.MouseUp, dgListener(&onMouseUp)); | |
149 | |
150 activateListener = dgListener(&onActivateListener); | |
151 deactivateListener = dgListener(&onDeactivateListener); | |
152 shell.addListener(DWT.Activate, activateListener); | |
153 shell.addListener(DWT.Deactivate, deactivateListener); | |
154 | |
155 addListener(DWT.Dispose, dgListener(&onDispose)); | |
156 } | |
157 | |
158 | |
159 private void restoreListener(Event event) | |
160 { | |
161 ishell.setMaximized(false); | |
162 } | |
163 | |
164 | |
165 private void minimizeListener(Event event) | |
166 { | |
167 ishell.setMinimized(true); | |
168 } | |
169 | |
170 | |
171 private void maximizeListener(Event event) | |
172 { | |
173 ishell.setMaximized(true); | |
174 } | |
175 | |
176 | |
177 private void closeListener(Event event) | |
178 { | |
179 ishell.close(); | |
180 } | |
181 | |
182 | |
183 private void onPaint(Event event) | |
184 { | |
185 Rectangle r = getClientArea(); | |
186 if(r.width is 0 || r.height is 0) return; | |
187 | |
188 bool active = (shell is display.getActiveShell() && ishell.isActiveShell()); | |
189 | |
190 GC gc = event.gc; | |
191 gc.setForeground(active ? gradStartColor : inactiveGradStartColor); | |
192 gc.setBackground(active ? gradEndColor : inactiveGradEndColor); | |
193 gc.fillGradientRectangle(r.x, r.y, r.width, r.height, false); | |
194 | |
195 int textLeftPadding = LEFT_PADDING; | |
196 if(image !is null) | |
197 { | |
198 Rectangle imageBounds = image.getBounds(); | |
199 if(imageBounds.width > IMAGE_SIZE || imageBounds.height > IMAGE_SIZE) | |
200 gc.drawImage(image, 0, 0, imageBounds.width, imageBounds.height, LEFT_PADDING, TOP_PADDING, IMAGE_SIZE, IMAGE_SIZE); | |
201 else | |
202 gc.drawImage(image, LEFT_PADDING + (IMAGE_SIZE-imageBounds.width)/2, (r.height-imageBounds.height)/2); | |
203 textLeftPadding += IMAGE_SIZE + LEFT_PADDING; | |
204 } | |
205 | |
206 if(text !is null && text.length() > 0) | |
207 { | |
208 gc.setForeground(active ? textColor : inactiveTextColor); | |
209 String s = text; | |
210 int availableWidth = r.width - textLeftPadding - RIGHT_PADDING; | |
211 if(gc.textExtent(s, DWT.DRAW_TRANSPARENT).x > availableWidth) | |
212 { | |
213 int ellipsisWidth = gc.textExtent(ELLIPSIS, DWT.DRAW_TRANSPARENT).x; | |
214 while(s.length() > 0) | |
215 { | |
216 s = s.substring(0, s.length()-1); | |
217 if(gc.textExtent(s, DWT.DRAW_TRANSPARENT).x + ellipsisWidth <= availableWidth) | |
218 { | |
219 s ~= ELLIPSIS; | |
220 break; | |
221 } | |
222 } | |
223 setToolTipText(text); | |
224 } | |
225 else setToolTipText(null); | |
226 if(s.length() > 0) gc.drawString(s, textLeftPadding, (r.height-gc.getFontMetrics().getHeight())/2, true); | |
227 } | |
228 else setToolTipText(null); | |
229 } | |
230 | |
231 | |
232 private void onMouseDown(Event event) | |
233 { | |
234 if(event.button is 1) | |
235 { | |
236 if(image !is null && event.x < LEFT_PADDING + IMAGE_SIZE) | |
237 { | |
238 cancelled = true; | |
239 // left-clicking on the image always shows the default popup menu | |
240 instrumentDefaultPopup(true); | |
241 defaultPopup.setLocation(toDisplay(0, getSize().y)); | |
242 defaultPopup.setVisible(true); | |
243 } | |
244 else | |
245 { | |
246 mouseDownOffsetX = event.x; | |
247 mouseDownOffsetY = event.y; | |
248 Point p = ishell.getLocation(); | |
249 snapBackX = p.x; | |
250 snapBackY = p.y; | |
251 cancelled = false; | |
252 } | |
253 } | |
254 else if(event.button is 3) | |
255 { | |
256 if((event.stateMask & DWT.BUTTON1) !is 0 && snapBackX !is Integer.MIN_VALUE && snapBackY !is Integer.MIN_VALUE) | |
257 { | |
258 ishell.setLocation(snapBackX, snapBackY); | |
259 snapBackX = Integer.MIN_VALUE; | |
260 snapBackY = Integer.MIN_VALUE; | |
261 cancelled = true; | |
262 } | |
263 else | |
264 { | |
265 } | |
266 } | |
267 } | |
268 | |
269 | |
270 private void onMenuDetect(Event event) | |
271 { | |
272 event.doit = false; | |
273 Menu m = getMenu(); | |
274 if(m is null || m.isDisposed()) | |
275 { | |
276 m = defaultPopup; | |
277 instrumentDefaultPopup(false); | |
278 } | |
279 m.setLocation(event.x, event.y); | |
280 m.setVisible(true); | |
281 } | |
282 | |
283 | |
284 private void onMouseDoubleClick(Event event) | |
285 { | |
286 if(event.button is 1) | |
287 { | |
288 if(image !is null && event.x < LEFT_PADDING + IMAGE_SIZE) | |
289 { | |
290 if(styleClose) ishell.close(); | |
291 } | |
292 else | |
293 { | |
294 if(styleMax) ishell.setMaximized(!ishell.getMaximized()); | |
295 } | |
296 cancelled = true; | |
297 } | |
298 } | |
299 | |
300 | |
301 private void onMouseMove(Event event) | |
302 { | |
303 if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0 && !ishell.getMaximized()) | |
304 { | |
305 if(timerTask !is null) | |
306 { | |
307 timerTask.cancel(); | |
308 timerTask = null; | |
309 } | |
310 long now = System.currentTimeMillis(); | |
311 if(lastUpdate + UPDATE_DELAY < now) | |
312 { | |
313 performMove(event); | |
314 lastUpdate = now; | |
315 } | |
316 else | |
317 { | |
318 timerTask = new class() TimerTask | |
319 { | |
320 public void run() | |
321 { | |
322 TimerTask executingTask = this; | |
323 event.display.asyncExec(new class() Runnable | |
324 { | |
325 public void run() | |
326 { | |
327 if(executingTask !is timerTask) return; | |
328 performMove(event); | |
329 } | |
330 }); | |
331 } | |
332 }; | |
333 timer.schedule(timerTask, UPDATE_DELAY); | |
334 } | |
335 } | |
336 } | |
337 | |
338 | |
339 private void onMouseUp(Event event) | |
340 { | |
341 if(ishell.getMaximized()) return; | |
342 if(image is null || event.x >= LEFT_PADDING + IMAGE_SIZE) | |
343 { | |
344 if(timerTask !is null) | |
345 { | |
346 timerTask.cancel(); | |
347 timerTask = null; | |
348 } | |
349 if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0) | |
350 { | |
351 performMove(event); | |
352 } | |
353 } | |
354 } | |
355 | |
356 | |
357 private void onActivateListener(Event event) | |
358 { | |
359 redraw(); | |
360 } | |
361 | |
362 | |
363 private void onDeactivateListener(Event event) | |
364 { | |
365 redraw(); | |
366 } | |
367 | |
368 | |
369 private void onDispose(Event event) | |
370 { | |
371 timer.cancel(); | |
372 shell.removeListener(DWT.Activate, activateListener); | |
373 shell.removeListener(DWT.Deactivate, deactivateListener); | |
374 closeImage.dispose(); | |
375 maximizeImage.dispose(); | |
376 restoreImage.dispose(); | |
377 minimizeImage.dispose(); | |
378 defaultPopup.dispose(); | |
379 } | |
380 | |
381 | |
382 private void performMove(Event event) | |
383 { | |
384 Point p = ishell.getLocation(); | |
385 int newX = p.x + event.x - mouseDownOffsetX; | |
386 int newY = p.y + event.y - mouseDownOffsetY; | |
387 | |
388 // Make sure that the minimum grab area stays visible | |
389 Rectangle deskCA = desktop.getClientArea(); | |
390 Rectangle bounds = getBounds(); | |
391 newX = Math.min(Math.max(newX, deskCA.x-bounds.x-bounds.width+MINIMUM_GRAB_AREA), deskCA.x-bounds.x+deskCA.width-minGrabSize.x); | |
392 newY = Math.min(Math.max(newY, deskCA.y-bounds.y-bounds.height+MINIMUM_GRAB_AREA), deskCA.y-bounds.y+deskCA.height-MINIMUM_GRAB_AREA); | |
393 | |
394 if(newX !is p.x || newY !is p.y) ishell.setLocation(newX, newY); | |
395 } | |
396 | |
397 | |
398 public Point getMinGrabSize() | |
399 { | |
400 return minGrabSize; | |
401 } | |
402 | |
403 | |
404 public Point computeSize(int wHint, int hHint, bool changed) | |
405 { | |
406 checkWidget(); | |
407 if(wHint is DWT.DEFAULT) wHint = 50; | |
408 if(hHint is DWT.DEFAULT) | |
409 { | |
410 GC gc = new GC(this); | |
411 hHint = gc.getFontMetrics().getHeight(); | |
412 hHint = Math.max(hHint, styleTool ? TOOL_SIZE : IMAGE_SIZE); | |
413 hHint += TOP_PADDING + BOTTOM_PADDING; | |
414 gc.dispose(); | |
415 } | |
416 return new Point(wHint, hHint); | |
417 } | |
418 | |
419 | |
420 private static int checkStyle(int style) | |
421 { | |
422 //int mask = DWT.SHADOW_IN | DWT.FLAT; | |
423 //style &= mask; | |
424 style = DWT.NO_FOCUS; | |
425 return style; | |
426 } | |
427 | |
428 | |
429 public bool setFocus() | |
430 { | |
431 checkWidget(); | |
432 return false; | |
433 } | |
434 | |
435 | |
436 public bool isReparentable () | |
437 { | |
438 checkWidget(); | |
439 return false; | |
440 } | |
441 | |
442 | |
443 public void setText(String text) | |
444 { | |
445 checkWidget(); | |
446 this.text = text; | |
447 redraw(); | |
448 } | |
449 | |
450 | |
451 public String getText() { return text; } | |
452 | |
453 | |
454 public void setImage(Image image) | |
455 { | |
456 checkWidget(); | |
457 if(styleTool) return; | |
458 this.image = image; | |
459 minGrabSize.x = MINIMUM_GRAB_AREA; | |
460 if(image !is null) minGrabSize.x += LEFT_PADDING + IMAGE_SIZE; | |
461 redraw(); | |
462 } | |
463 | |
464 | |
465 public Image getImage() { return image; } | |
466 | |
467 | |
468 private Font createTitleFont(Font f, bool tool) | |
469 { | |
470 FontData[] fds = f.getFontData(); | |
471 foreach(fd; fds) | |
472 { | |
473 fd.setStyle(fd.getStyle() | DWT.BOLD); | |
474 if(tool) fd.setHeight(cast(int)(fd.getHeight()*0.9)); | |
475 } | |
476 return new Font(getDisplay(), fds); | |
477 } | |
478 | |
479 | |
480 private void instrumentDefaultPopup(bool onImage) | |
481 { | |
482 restoreItem.setEnabled(styleMax && ishell.getMaximized()); | |
483 maximizeItem.setEnabled(styleMax && !ishell.getMaximized()); | |
484 MenuItem def = null; | |
485 if(onImage) | |
486 { | |
487 if(styleClose) def = closeItem; | |
488 } | |
489 else if(styleMax) | |
490 { | |
491 def = ishell.getMaximized() ? restoreItem : maximizeItem; | |
492 } | |
493 defaultPopup.setDefaultItem(def); | |
494 } | |
495 | |
496 | |
497 private static const int IMAGE_TYPE_CLOSE = 1; | |
498 private static const int IMAGE_TYPE_MAXIMIZE = 2; | |
499 private static const int IMAGE_TYPE_RESTORE = 3; | |
500 private static const int IMAGE_TYPE_MINIMIZE = 4; | |
501 | |
502 | |
503 private Image createMenuImage(int type, int height) | |
504 { | |
505 final Point size = new Point(height, height); | |
506 final int imgWidth = height + height/2; | |
507 final Color fg = getForeground(); | |
508 final Color white = getDisplay().getSystemColor(DWT.COLOR_WHITE); | |
509 final RGB blackRGB = new RGB(0,0,0); | |
510 | |
511 ImageData id = new ImageData(imgWidth, size.y, 1, new PaletteData([ blackRGB, fg.getRGB() ])); | |
512 ImageData maskid = new ImageData(imgWidth, size.y, 1, new PaletteData([ blackRGB, white.getRGB() ])); | |
513 | |
514 Image img = new Image(getDisplay(), id); | |
515 GC gc = new GC(img); | |
516 gc.setForeground(fg); | |
517 drawMenuImage(gc, size, type); | |
518 gc.dispose(); | |
519 | |
520 Image maskimg = new Image(getDisplay(), maskid); | |
521 gc = new GC(maskimg); | |
522 gc.setForeground(white); | |
523 drawMenuImage(gc, size, type); | |
524 gc.dispose(); | |
525 | |
526 Image transp = new Image(getDisplay(), img.getImageData(), maskimg.getImageData()); | |
527 img.dispose(); | |
528 maskimg.dispose(); | |
529 return transp; | |
530 } | |
531 | |
532 | |
533 private void drawMenuImage(GC gc, Point size, int type) | |
534 { | |
535 switch(type) | |
536 { | |
537 case IMAGE_TYPE_CLOSE: | |
538 gc.drawLine(1, 1, size.x-2, size.y-2); | |
539 gc.drawLine(2, 1, size.x-2, size.y-3); | |
540 gc.drawLine(1, 2, size.x-3, size.y-2); | |
541 gc.drawLine(1, size.y-2, size.x-2, 1); | |
542 gc.drawLine(1, size.y-3, size.x-3, 1); | |
543 gc.drawLine(2, size.y-2, size.x-2, 2); | |
544 break; | |
545 | |
546 case IMAGE_TYPE_RESTORE: | |
547 gc.drawRectangle(0, 4, size.x-4, size.y-6); | |
548 gc.drawLine(1, 5, size.x-5, 5); | |
549 gc.drawLine(2, 1, size.x-2, 1); | |
550 gc.drawLine(2, 2, size.x-2, 2); | |
551 gc.drawPoint(2, 3); | |
552 gc.drawLine(size.x-2, 3, size.x-2, size.y-5); | |
553 gc.drawPoint(size.x-3, size.y-5); | |
554 break; | |
555 | |
556 case IMAGE_TYPE_MAXIMIZE: | |
557 gc.drawRectangle(0, 0, size.x-2, size.y-2); | |
558 gc.drawLine(1, 1, size.x-3, 1); | |
559 break; | |
560 | |
561 case IMAGE_TYPE_MINIMIZE: | |
562 gc.drawLine(1, size.y-2, size.x-4, size.y-2); | |
563 gc.drawLine(1, size.y-3, size.x-4, size.y-3); | |
564 break; | |
565 } | |
566 } | |
567 } |