2 * Copyright 2005-2009, Haiku Inc.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
7 * Stephan Aßmus <superstippi@gmx.de>
11 #include "WorkspacesView.h"
13 #include "AppServer.h"
14 #include "Decorator.h"
16 #include "DrawingEngine.h"
17 #include "DrawState.h"
18 #include "InterfaceDefs.h"
19 #include "ServerApp.h"
21 #include "Workspace.h"
23 #include <WindowPrivate.h>
26 WorkspacesView::WorkspacesView(BRect frame
, BPoint scrollingOffset
,
27 const char* name
, int32 token
, uint32 resizeMode
, uint32 flags
)
29 View(frame
, scrollingOffset
, name
, token
, resizeMode
, flags
),
30 fSelectedWindow(NULL
),
31 fSelectedWorkspace(-1),
34 fDrawState
->SetLowColor((rgb_color
){ 255, 255, 255, 255 });
35 fDrawState
->SetHighColor((rgb_color
){ 0, 0, 0, 255 });
39 WorkspacesView::~WorkspacesView()
45 WorkspacesView::AttachedToWindow(::Window
* window
)
47 View::AttachedToWindow(window
);
49 window
->AddWorkspacesView();
50 window
->Desktop()->AddWorkspacesView(this);
55 WorkspacesView::DetachedFromWindow()
57 fWindow
->Desktop()->RemoveWorkspacesView(this);
58 fWindow
->RemoveWorkspacesView();
60 View::DetachedFromWindow();
65 WorkspacesView::_GetGrid(int32
& columns
, int32
& rows
)
67 DesktopSettings
settings(Window()->Desktop());
69 columns
= settings
.WorkspacesColumns();
70 rows
= settings
.WorkspacesRows();
74 /*! \brief Returns the frame of the screen for the specified workspace.
77 WorkspacesView::_ScreenFrame(int32 i
)
79 return Window()->Desktop()->WorkspaceFrame(i
);
83 /*! \brief Returns the frame of the specified workspace within the
87 WorkspacesView::_WorkspaceAt(int32 i
)
90 _GetGrid(columns
, rows
);
92 BRect frame
= Bounds();
93 LocalToScreenTransform().Apply(&frame
);
95 int32 width
= frame
.IntegerWidth() / columns
;
96 int32 height
= frame
.IntegerHeight() / rows
;
98 int32 column
= i
% columns
;
99 int32 row
= i
/ columns
;
101 BRect
rect(column
* width
, row
* height
, (column
+ 1) * width
,
104 rect
.OffsetBy(frame
.LeftTop());
106 // make sure there is no gap anywhere
107 if (column
== columns
- 1)
108 rect
.right
= frame
.right
;
110 rect
.bottom
= frame
.bottom
;
116 /*! \brief Returns the workspace frame and index of the workspace
119 If, for some reason, there is no workspace located under \where,
120 an empty rectangle is returned, and \a index is set to -1.
123 WorkspacesView::_WorkspaceAt(BPoint where
, int32
& index
)
126 _GetGrid(columns
, rows
);
128 for (index
= columns
* rows
; index
-- > 0;) {
129 BRect workspaceFrame
= _WorkspaceAt(index
);
131 if (workspaceFrame
.Contains(where
))
132 return workspaceFrame
;
140 WorkspacesView::_WindowFrame(const BRect
& workspaceFrame
,
141 const BRect
& screenFrame
, const BRect
& windowFrame
,
142 BPoint windowPosition
)
144 BRect frame
= windowFrame
;
145 frame
.OffsetTo(windowPosition
);
147 // scale down the rect
148 float factor
= workspaceFrame
.Width() / screenFrame
.Width();
149 frame
.left
= frame
.left
* factor
;
150 frame
.right
= frame
.right
* factor
;
152 factor
= workspaceFrame
.Height() / screenFrame
.Height();
153 frame
.top
= frame
.top
* factor
;
154 frame
.bottom
= frame
.bottom
* factor
;
156 // offset by the workspace fame position
157 // and snap to integer coordinates without distorting the size too much
158 frame
.OffsetTo(rintf(frame
.left
+ workspaceFrame
.left
),
159 rintf(frame
.top
+ workspaceFrame
.top
));
160 frame
.right
= rintf(frame
.right
);
161 frame
.bottom
= rintf(frame
.bottom
);
168 WorkspacesView::_DrawWindow(DrawingEngine
* drawingEngine
,
169 const BRect
& workspaceFrame
, const BRect
& screenFrame
, ::Window
* window
,
170 BPoint windowPosition
, BRegion
& backgroundRegion
, bool workspaceActive
)
172 if (window
->Feel() == kDesktopWindowFeel
|| window
->IsHidden())
175 BPoint offset
= window
->Frame().LeftTop() - windowPosition
;
176 BRect frame
= _WindowFrame(workspaceFrame
, screenFrame
, window
->Frame(),
178 Decorator
*decorator
= window
->Decorator();
179 BRect
tabFrame(0, 0, 0, 0);
180 if (decorator
!= NULL
)
181 tabFrame
= decorator
->TitleBarRect();
183 tabFrame
= _WindowFrame(workspaceFrame
, screenFrame
,
184 tabFrame
, tabFrame
.LeftTop() - offset
);
185 if (!workspaceFrame
.Intersects(frame
)
186 && !workspaceFrame
.Intersects(tabFrame
))
189 rgb_color activeTabColor
= (rgb_color
){ 255, 203, 0, 255 };
190 rgb_color inactiveTabColor
= (rgb_color
){ 232, 232, 232, 255 };
191 rgb_color navColor
= (rgb_color
){ 0, 0, 229, 255 };
192 rgb_color activeFrameColor
= (rgb_color
){ 180, 180, 180, 255 };
193 rgb_color inactiveFrameColor
= (rgb_color
){ 180, 180, 180, 255 };
194 if (decorator
!= NULL
) {
195 activeTabColor
= decorator
->UIColor(B_WINDOW_TAB_COLOR
);
196 inactiveTabColor
= decorator
->UIColor(B_WINDOW_INACTIVE_TAB_COLOR
);
197 navColor
= decorator
->UIColor(B_NAVIGATION_BASE_COLOR
);
198 activeFrameColor
= decorator
->UIColor(B_WINDOW_BORDER_COLOR
);
199 inactiveFrameColor
= decorator
->UIColor(B_WINDOW_INACTIVE_BORDER_COLOR
);
202 rgb_color white
= (rgb_color
){ 255, 255, 255, 255 };
204 rgb_color tabColor
= inactiveTabColor
;
205 rgb_color frameColor
= inactiveFrameColor
;
206 if (window
->IsFocus()) {
207 tabColor
= activeTabColor
;
208 frameColor
= activeFrameColor
;
211 if (!workspaceActive
) {
212 _DarkenColor(tabColor
);
213 _DarkenColor(frameColor
);
217 if (tabFrame
.Height() > 0 && tabFrame
.Width() > 0) {
218 float width
= tabFrame
.Width();
219 if (tabFrame
.left
< frame
.left
) {
220 // Shift the tab right
221 tabFrame
.left
= frame
.left
;
222 tabFrame
.right
= tabFrame
.left
+ width
;
224 if (tabFrame
.right
> frame
.right
) {
225 // Shift the tab left
226 tabFrame
.right
= frame
.right
;
227 tabFrame
.left
= tabFrame
.right
- width
;
230 if (tabFrame
.bottom
>= tabFrame
.top
) {
232 float tabHeight
= tabFrame
.Height();
233 tabFrame
.bottom
= frame
.top
- 1;
234 tabFrame
.top
= tabFrame
.bottom
- tabHeight
;
237 tabFrame
= tabFrame
& workspaceFrame
;
239 if (decorator
!= NULL
&& tabFrame
.IsValid()) {
240 drawingEngine
->FillRect(tabFrame
, tabColor
);
241 backgroundRegion
.Exclude(tabFrame
);
245 drawingEngine
->StrokeRect(frame
, frameColor
);
247 BRect fillFrame
= frame
.InsetByCopy(1, 1) & workspaceFrame
;
248 frame
= frame
& workspaceFrame
;
249 if (!frame
.IsValid())
252 // fill the window itself
253 drawingEngine
->FillRect(fillFrame
, white
);
257 // TODO: the mini-window functionality should probably be moved into the
258 // Window class, so that it has only to be recalculated on demand. With
259 // double buffered windows, this would also open up the door to have a
260 // more detailed preview.
261 BString
title(window
->Title());
263 const ServerFont
& font
= fDrawState
->Font();
265 font
.TruncateString(&title
, B_TRUNCATE_END
, fillFrame
.Width() - 4);
266 font_height fontHeight
;
267 font
.GetHeight(fontHeight
);
268 float height
= ceilf(fontHeight
.ascent
) + ceilf(fontHeight
.descent
);
269 if (title
.Length() > 0 && height
< frame
.Height() - 2) {
270 rgb_color textColor
= tint_color(white
, B_DARKEN_4_TINT
);
271 drawingEngine
->SetHighColor(textColor
);
272 drawingEngine
->SetLowColor(white
);
274 float width
= font
.StringWidth(title
.String(), title
.Length());
277 textOffset
.x
= rintf(frame
.left
+ (frame
.Width() - width
) / 2);
278 textOffset
.y
= rintf(frame
.top
+ (frame
.Height() - height
) / 2
279 + fontHeight
.ascent
);
280 drawingEngine
->DrawString(title
.String(), title
.Length(), textOffset
);
283 // prevent the next window down from drawing over this window
284 backgroundRegion
.Exclude(frame
);
289 WorkspacesView::_DrawWorkspace(DrawingEngine
* drawingEngine
,
290 BRegion
& redraw
, int32 index
)
292 BRect rect
= _WorkspaceAt(index
);
294 Workspace
workspace(*Window()->Desktop(), index
, true);
295 bool workspaceActive
= workspace
.IsCurrent();
296 if (workspaceActive
) {
298 rgb_color black
= (rgb_color
){ 0, 0, 0, 255 };
299 drawingEngine
->StrokeRect(rect
, black
);
300 } else if (index
== fSelectedWorkspace
) {
301 rgb_color gray
= (rgb_color
){ 80, 80, 80, 255 };
302 drawingEngine
->StrokeRect(rect
, gray
);
307 rgb_color color
= workspace
.Color();
308 if (!workspaceActive
)
313 BRegion backgroundRegion
= redraw
;
315 // TODO: would be nice to get the real update region here
317 BRect screenFrame
= _ScreenFrame(index
);
319 BRegion
workspaceRegion(rect
);
320 backgroundRegion
.IntersectWith(&workspaceRegion
);
321 drawingEngine
->ConstrainClippingRegion(&backgroundRegion
);
323 ServerFont font
= fDrawState
->Font();
324 font
.SetSize(fWindow
->ServerWindow()->App()->PlainFont().Size());
325 float reducedSize
= ceilf(max_c(8.0f
,
326 min_c(Frame().Height(), Frame().Width()) / 15));
327 if (font
.Size() > reducedSize
)
328 font
.SetSize(reducedSize
);
329 fDrawState
->SetFont(font
);
330 drawingEngine
->SetFont(font
);
332 // We draw from top down and cut the window out of the clipping region
333 // which reduces the flickering
336 while (workspace
.GetPreviousWindow(window
, leftTop
) == B_OK
) {
337 _DrawWindow(drawingEngine
, rect
, screenFrame
, window
,
338 leftTop
, backgroundRegion
, workspaceActive
);
342 drawingEngine
->FillRect(rect
, color
);
344 drawingEngine
->ConstrainClippingRegion(&redraw
);
349 WorkspacesView::_DarkenColor(rgb_color
& color
) const
351 color
= tint_color(color
, B_DARKEN_2_TINT
);
356 WorkspacesView::_Invalidate() const
358 BRect frame
= Bounds();
359 LocalToScreenTransform().Apply(&frame
);
361 BRegion
region(frame
);
362 Window()->MarkContentDirty(region
);
367 WorkspacesView::Draw(DrawingEngine
* drawingEngine
, BRegion
* effectiveClipping
,
368 BRegion
* windowContentClipping
, bool deep
)
370 // we can only draw within our own area
371 BRegion
redraw(ScreenAndUserClipping(windowContentClipping
));
372 // add the current clipping
373 redraw
.IntersectWith(effectiveClipping
);
376 _GetGrid(columns
, rows
);
380 // make sure the grid around the active workspace is not drawn
382 BRect activeRect
= _WorkspaceAt(Window()->Desktop()->CurrentWorkspace());
383 BRegion
gridRegion(redraw
);
384 gridRegion
.Exclude(activeRect
);
385 drawingEngine
->ConstrainClippingRegion(&gridRegion
);
387 BRect frame
= Bounds();
388 LocalToScreenTransform().Apply(&frame
);
392 drawingEngine
->StrokeLine(BPoint(frame
.left
, frame
.top
),
393 BPoint(frame
.right
, frame
.top
), ViewColor());
395 for (int32 row
= 0; row
< rows
; row
++) {
396 BRect rect
= _WorkspaceAt(row
* columns
);
397 drawingEngine
->StrokeLine(BPoint(frame
.left
, rect
.bottom
),
398 BPoint(frame
.right
, rect
.bottom
), ViewColor());
403 drawingEngine
->StrokeLine(BPoint(frame
.left
, frame
.top
),
404 BPoint(frame
.left
, frame
.bottom
), ViewColor());
406 for (int32 column
= 0; column
< columns
; column
++) {
407 BRect rect
= _WorkspaceAt(column
);
408 drawingEngine
->StrokeLine(BPoint(rect
.right
, frame
.top
),
409 BPoint(rect
.right
, frame
.bottom
), ViewColor());
412 drawingEngine
->ConstrainClippingRegion(&redraw
);
416 for (int32 i
= rows
* columns
; i
-- > 0;) {
417 _DrawWorkspace(drawingEngine
, redraw
, i
);
419 fWindow
->ServerWindow()->ResyncDrawState();
424 WorkspacesView::MouseDown(BMessage
* message
, BPoint where
)
426 // reset tracking variables
427 fSelectedWorkspace
= -1;
428 fSelectedWindow
= NULL
;
431 // check if the correct mouse button is pressed
433 if (message
->FindInt32("buttons", &buttons
) != B_OK
434 || (buttons
& B_PRIMARY_MOUSE_BUTTON
) == 0)
438 BRect workspaceFrame
= _WorkspaceAt(where
, index
);
442 Workspace
workspace(*Window()->Desktop(), index
);
443 workspaceFrame
.InsetBy(1, 1);
445 BRect screenFrame
= _ScreenFrame(index
);
450 while (workspace
.GetPreviousWindow(window
, leftTop
) == B_OK
) {
451 if (window
->IsMinimized() || window
->IsHidden())
454 BRect frame
= _WindowFrame(workspaceFrame
, screenFrame
, window
->Frame(),
456 if (frame
.Contains(where
) && window
->Feel() != kDesktopWindowFeel
457 && window
->Feel() != kWindowScreenFeel
) {
458 fSelectedWindow
= window
;
464 // Some special functionality (clicked with modifiers)
467 if (fSelectedWindow
!= NULL
468 && message
->FindInt32("modifiers", &modifiers
) == B_OK
) {
469 if ((modifiers
& B_CONTROL_KEY
) != 0) {
470 // Activate window if clicked with the control key pressed,
471 // minimize it if control+shift - this mirrors Deskbar
472 // shortcuts (when pressing a team menu item).
473 if ((modifiers
& B_SHIFT_KEY
) != 0)
474 fSelectedWindow
->ServerWindow()->NotifyMinimize(true);
476 Window()->Desktop()->ActivateWindow(fSelectedWindow
);
477 fSelectedWindow
= NULL
;
478 } else if ((modifiers
& B_OPTION_KEY
) != 0) {
479 // Also, send window to back if clicked with the option
481 Window()->Desktop()->SendWindowBehind(fSelectedWindow
);
482 fSelectedWindow
= NULL
;
486 // If this window is movable, we keep it selected
487 // (we prevent our own window from being moved, too)
489 if ((fSelectedWindow
!= NULL
490 && (fSelectedWindow
->Flags() & B_NOT_MOVABLE
) != 0)
491 || fSelectedWindow
== Window()) {
492 fSelectedWindow
= NULL
;
495 fLeftTopOffset
= where
- windowFrame
.LeftTop();
496 fSelectedWorkspace
= index
;
505 WorkspacesView::MouseUp(BMessage
* message
, BPoint where
)
507 if (!fHasMoved
&& fSelectedWorkspace
>= 0) {
509 _WorkspaceAt(where
, index
);
511 Window()->Desktop()->SetWorkspaceAsync(index
);
514 if (fSelectedWindow
!= NULL
) {
515 // We need to hide the selection frame again
519 fSelectedWindow
= NULL
;
520 fSelectedWorkspace
= -1;
525 WorkspacesView::MouseMoved(BMessage
* message
, BPoint where
)
527 if (fSelectedWindow
== NULL
&& fSelectedWorkspace
< 0)
530 // check if the correct mouse button is pressed
532 if (message
->FindInt32("buttons", &buttons
) != B_OK
533 || (buttons
& B_PRIMARY_MOUSE_BUTTON
) == 0)
537 Window()->Desktop()->SetMouseEventWindow(Window());
538 // don't let us off the mouse
542 BRect workspaceFrame
= _WorkspaceAt(where
, index
);
544 if (fSelectedWindow
== NULL
) {
545 if (fSelectedWorkspace
>= 0 && fSelectedWorkspace
!= index
) {
546 fSelectedWorkspace
= index
;
552 workspaceFrame
.InsetBy(1, 1);
554 if (index
!= fSelectedWorkspace
) {
555 if (fSelectedWindow
->IsNormal() && !fSelectedWindow
->InWorkspace(index
)) {
556 // move window to this new workspace
557 uint32 newWorkspaces
= (fSelectedWindow
->Workspaces()
558 & ~(1UL << fSelectedWorkspace
)) | (1UL << index
);
560 Window()->Desktop()->SetWindowWorkspaces(fSelectedWindow
,
563 fSelectedWorkspace
= index
;
566 BRect screenFrame
= _ScreenFrame(index
);
567 float left
= rintf((where
.x
- workspaceFrame
.left
- fLeftTopOffset
.x
)
568 * screenFrame
.Width() / workspaceFrame
.Width());
569 float top
= rintf((where
.y
- workspaceFrame
.top
- fLeftTopOffset
.y
)
570 * screenFrame
.Height() / workspaceFrame
.Height());
573 if (fSelectedWorkspace
== Window()->Desktop()->CurrentWorkspace())
574 leftTop
= fSelectedWindow
->Frame().LeftTop();
576 if (fSelectedWindow
->Anchor(fSelectedWorkspace
).position
577 == kInvalidWindowPosition
) {
578 fSelectedWindow
->Anchor(fSelectedWorkspace
).position
579 = fSelectedWindow
->Frame().LeftTop();
581 leftTop
= fSelectedWindow
->Anchor(fSelectedWorkspace
).position
;
584 // Don't treat every little mouse move as a window move - this would
585 // make it too hard to activate a workspace.
586 float diff
= max_c(fabs(fClickPoint
.x
- where
.x
),
587 fabs(fClickPoint
.y
- where
.y
));
588 if (!fHasMoved
&& diff
> 2)
592 Window()->Desktop()->MoveWindowBy(fSelectedWindow
, left
- leftTop
.x
,
593 top
- leftTop
.y
, fSelectedWorkspace
);
599 WorkspacesView::WindowChanged(::Window
* window
)
601 // TODO: be smarter about this!
607 WorkspacesView::WindowRemoved(::Window
* window
)
609 if (fSelectedWindow
== window
)
610 fSelectedWindow
= NULL
;