91d6453948
list in the other direction. * Since WorkspacesLayer now cuts out the current window from the clipping region, the window order was upside down; it now uses the new Workspace::GetPreviousWindow(). This fixes bug #1105. * WorkspacesLayer::MouseDown() now also uses GetPreviousWindow() which prevents it from needing to scan the whole window list for the top window at every click. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20382 a95241bf-73f2-0310-859d-f6bbb57e9c96
527 lines
13 KiB
C++
527 lines
13 KiB
C++
/*
|
|
* Copyright 2005-2007, Haiku Inc.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Axel Dörfler, axeld@pinc-software.de
|
|
* Stephan Aßmus <superstippi@gmx.de>
|
|
*/
|
|
|
|
|
|
#include "WorkspacesLayer.h"
|
|
|
|
#include "AppServer.h"
|
|
#include "Desktop.h"
|
|
#include "DrawingEngine.h"
|
|
#include "WindowLayer.h"
|
|
#include "Workspace.h"
|
|
|
|
#include <WindowPrivate.h>
|
|
|
|
|
|
WorkspacesLayer::WorkspacesLayer(BRect frame, BPoint scrollingOffset,
|
|
const char* name, int32 token, uint32 resizeMode, uint32 flags)
|
|
: ViewLayer(frame, scrollingOffset, name, token, resizeMode, flags),
|
|
fSelectedWindow(NULL),
|
|
fSelectedWorkspace(-1),
|
|
fHasMoved(false)
|
|
{
|
|
fDrawState->SetLowColor(RGBColor(255, 255, 255));
|
|
fDrawState->SetHighColor(RGBColor(0, 0, 0));
|
|
}
|
|
|
|
|
|
WorkspacesLayer::~WorkspacesLayer()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::_GetGrid(int32& columns, int32& rows)
|
|
{
|
|
DesktopSettings settings(Window()->Desktop());
|
|
|
|
int32 count = settings.WorkspacesCount();
|
|
int32 squareRoot = (int32)sqrt(count);
|
|
|
|
rows = 1;
|
|
for (int32 i = 2; i <= squareRoot; i++) {
|
|
if (count % i == 0)
|
|
rows = i;
|
|
}
|
|
|
|
columns = count / rows;
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Returns the frame of the screen for the specified workspace.
|
|
*/
|
|
BRect
|
|
WorkspacesLayer::_ScreenFrame(int32 i)
|
|
{
|
|
return Window()->Desktop()->VirtualScreen().Frame();
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Returns the frame of the specified workspace within the
|
|
workspaces layer.
|
|
*/
|
|
BRect
|
|
WorkspacesLayer::_WorkspaceAt(int32 i)
|
|
{
|
|
int32 columns, rows;
|
|
_GetGrid(columns, rows);
|
|
|
|
int32 width = Frame().IntegerWidth() / columns;
|
|
int32 height = Frame().IntegerHeight() / rows;
|
|
|
|
int32 column = i % columns;
|
|
int32 row = i / columns;
|
|
|
|
BRect rect(column * width, row * height, (column + 1) * width, (row + 1) * height);
|
|
|
|
rect.OffsetBy(Frame().LeftTop());
|
|
|
|
// make sure there is no gap anywhere
|
|
if (column == columns - 1)
|
|
rect.right = Frame().right;
|
|
if (row == rows - 1)
|
|
rect.bottom = Frame().bottom;
|
|
|
|
return rect;
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Returns the workspace frame and index of the workspace
|
|
under \a where.
|
|
|
|
If, for some reason, there is no workspace located under \where,
|
|
an empty rectangle is returned, and \a index is set to -1.
|
|
*/
|
|
BRect
|
|
WorkspacesLayer::_WorkspaceAt(BPoint where, int32& index)
|
|
{
|
|
int32 columns, rows;
|
|
_GetGrid(columns, rows);
|
|
|
|
for (index = columns * rows; index-- > 0;) {
|
|
BRect workspaceFrame = _WorkspaceAt(index);
|
|
|
|
if (workspaceFrame.Contains(where))
|
|
return workspaceFrame;
|
|
}
|
|
|
|
return BRect();
|
|
}
|
|
|
|
|
|
BRect
|
|
WorkspacesLayer::_WindowFrame(const BRect& workspaceFrame,
|
|
const BRect& screenFrame, const BRect& windowFrame,
|
|
BPoint windowPosition)
|
|
{
|
|
BRect frame = windowFrame;
|
|
frame.OffsetTo(windowPosition);
|
|
|
|
float factor = workspaceFrame.Width() / screenFrame.Width();
|
|
frame.left = rintf(frame.left * factor);
|
|
frame.right = rintf(frame.right * factor);
|
|
|
|
factor = workspaceFrame.Height() / screenFrame.Height();
|
|
frame.top = rintf(frame.top * factor);
|
|
frame.bottom = rintf(frame.bottom * factor);
|
|
|
|
frame.OffsetBy(workspaceFrame.LeftTop());
|
|
return frame;
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::_DrawWindow(DrawingEngine* drawingEngine, const BRect& workspaceFrame,
|
|
const BRect& screenFrame, WindowLayer* window, BPoint windowPosition,
|
|
BRegion& backgroundRegion, bool active)
|
|
{
|
|
if (window->Feel() == kDesktopWindowFeel || window->IsHidden())
|
|
return;
|
|
|
|
BPoint offset = window->Frame().LeftTop() - windowPosition;
|
|
BRect frame = _WindowFrame(workspaceFrame, screenFrame, window->Frame(),
|
|
windowPosition);
|
|
Decorator *decorator = window->Decorator();
|
|
BRect tabFrame(0, 0, 0, 0);
|
|
if (decorator != NULL)
|
|
tabFrame = decorator->TabRect();
|
|
|
|
tabFrame = _WindowFrame(workspaceFrame, screenFrame,
|
|
tabFrame, tabFrame.LeftTop() - offset);
|
|
if (!workspaceFrame.Intersects(frame) && !workspaceFrame.Intersects(tabFrame))
|
|
return;
|
|
|
|
// ToDo: let decorator do this!
|
|
RGBColor yellow;
|
|
if (decorator != NULL)
|
|
yellow = decorator->UIColor(B_WINDOW_TAB_COLOR);
|
|
RGBColor frameColor(180, 180, 180);
|
|
RGBColor white(255, 255, 255);
|
|
|
|
if (!active) {
|
|
_DarkenColor(yellow);
|
|
_DarkenColor(frameColor);
|
|
_DarkenColor(white);
|
|
}
|
|
if (window == fSelectedWindow) {
|
|
// TODO: what about standard navigation color here?
|
|
frameColor.SetColor(80, 80, 80);
|
|
}
|
|
|
|
if (tabFrame.left < frame.left)
|
|
tabFrame.left = frame.left;
|
|
if (tabFrame.right >= frame.right)
|
|
tabFrame.right = frame.right - 1;
|
|
|
|
tabFrame.top = frame.top - 1;
|
|
tabFrame.bottom = frame.top - 1;
|
|
tabFrame = tabFrame & workspaceFrame;
|
|
|
|
if (decorator != NULL && tabFrame.IsValid()) {
|
|
drawingEngine->StrokeLine(tabFrame.LeftTop(), tabFrame.RightBottom(), yellow);
|
|
backgroundRegion.Exclude(tabFrame);
|
|
}
|
|
|
|
drawingEngine->StrokeRect(frame, frameColor);
|
|
|
|
frame = frame & workspaceFrame;
|
|
if (frame.IsValid()) {
|
|
drawingEngine->FillRect(frame.InsetByCopy(1, 1), white);
|
|
backgroundRegion.Exclude(frame);
|
|
}
|
|
|
|
// draw title
|
|
|
|
// TODO: disabled because it's much too slow this way - the mini-window
|
|
// functionality should probably be moved into the WindowLayer class,
|
|
// so that it has only to be recalculated on demand. With double buffered
|
|
// windows, this would also open up the door to have a more detailed
|
|
// preview.
|
|
#if 0
|
|
BString title(window->Title());
|
|
|
|
ServerFont font = fDrawState->Font();
|
|
font.SetSize(7);
|
|
fDrawState->SetFont(font);
|
|
|
|
fDrawState->Font().TruncateString(&title, B_TRUNCATE_END, frame.Width() - 4);
|
|
float width = drawingEngine->StringWidth(title.String(), title.Length(),
|
|
fDrawState, NULL);
|
|
float height = drawingEngine->StringHeight(title.String(), title.Length(),
|
|
fDrawState);
|
|
|
|
drawingEngine->DrawString(title.String(), title.Length(),
|
|
BPoint(frame.left + (frame.Width() - width) / 2,
|
|
frame.top + (frame.Height() + height) / 2),
|
|
fDrawState, NULL);
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::_DrawWorkspace(DrawingEngine* drawingEngine,
|
|
BRegion& redraw, int32 index)
|
|
{
|
|
BRect rect = _WorkspaceAt(index);
|
|
|
|
Workspace workspace(*Window()->Desktop(), index);
|
|
bool active = workspace.IsCurrent();
|
|
if (active) {
|
|
// draw active frame
|
|
RGBColor black(0, 0, 0);
|
|
drawingEngine->StrokeRect(rect, black);
|
|
} else if (index == fSelectedWorkspace) {
|
|
RGBColor gray(80, 80, 80);
|
|
drawingEngine->StrokeRect(rect, gray);
|
|
}
|
|
|
|
rect.InsetBy(1, 1);
|
|
|
|
RGBColor color = workspace.Color();
|
|
if (!active)
|
|
_DarkenColor(color);
|
|
|
|
// draw windows
|
|
|
|
BRegion backgroundRegion = redraw;
|
|
|
|
// ToDo: would be nice to get the real update region here
|
|
|
|
BRect screenFrame = _ScreenFrame(index);
|
|
|
|
BRegion workspaceRegion(rect);
|
|
backgroundRegion.IntersectWith(&workspaceRegion);
|
|
drawingEngine->ConstrainClippingRegion(&backgroundRegion);
|
|
|
|
// We draw from top down and cut the window out of the clipping region
|
|
// which reduces the flickering
|
|
WindowLayer* window;
|
|
BPoint leftTop;
|
|
while (workspace.GetPreviousWindow(window, leftTop) == B_OK) {
|
|
_DrawWindow(drawingEngine, rect, screenFrame, window,
|
|
leftTop, backgroundRegion, active);
|
|
}
|
|
|
|
// draw background
|
|
drawingEngine->FillRect(rect, color);
|
|
|
|
drawingEngine->ConstrainClippingRegion(&redraw);
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::_DarkenColor(RGBColor& color) const
|
|
{
|
|
color = tint_color(color.GetColor32(), B_DARKEN_2_TINT);
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::_Invalidate() const
|
|
{
|
|
BRegion region(Frame());
|
|
Window()->MarkContentDirty(region);
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
|
|
BRegion* windowContentClipping, bool deep)
|
|
{
|
|
// we can only draw within our own area
|
|
BRegion redraw(ScreenClipping(windowContentClipping));
|
|
// add the current clipping
|
|
redraw.IntersectWith(effectiveClipping);
|
|
|
|
int32 columns, rows;
|
|
_GetGrid(columns, rows);
|
|
|
|
// draw grid
|
|
|
|
// make sure the grid around the active workspace is not drawn
|
|
// to reduce flicker
|
|
BRect activeRect = _WorkspaceAt(Window()->Desktop()->CurrentWorkspace());
|
|
BRegion gridRegion(redraw);
|
|
gridRegion.Exclude(activeRect);
|
|
drawingEngine->ConstrainClippingRegion(&gridRegion);
|
|
|
|
BRect frame = Frame();
|
|
// top ViewLayer frame is in screen coordinates
|
|
|
|
// horizontal lines
|
|
|
|
drawingEngine->StrokeLine(BPoint(frame.left, frame.top),
|
|
BPoint(frame.right, frame.top), ViewColor());
|
|
|
|
for (int32 row = 0; row < rows; row++) {
|
|
BRect rect = _WorkspaceAt(row * columns);
|
|
drawingEngine->StrokeLine(BPoint(frame.left, rect.bottom),
|
|
BPoint(frame.right, rect.bottom), ViewColor());
|
|
}
|
|
|
|
// vertical lines
|
|
|
|
drawingEngine->StrokeLine(BPoint(frame.left, frame.top),
|
|
BPoint(frame.left, frame.bottom), ViewColor());
|
|
|
|
for (int32 column = 0; column < columns; column++) {
|
|
BRect rect = _WorkspaceAt(column);
|
|
drawingEngine->StrokeLine(BPoint(rect.right, frame.top),
|
|
BPoint(rect.right, frame.bottom), ViewColor());
|
|
}
|
|
|
|
drawingEngine->ConstrainClippingRegion(&redraw);
|
|
|
|
// draw workspaces
|
|
|
|
for (int32 i = rows * columns; i-- > 0;) {
|
|
_DrawWorkspace(drawingEngine, redraw, i);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::MouseDown(BMessage* message, BPoint where)
|
|
{
|
|
// reset tracking variables
|
|
fSelectedWorkspace = -1;
|
|
fSelectedWindow = NULL;
|
|
fHasMoved = false;
|
|
|
|
// check if the correct mouse button is pressed
|
|
int32 buttons;
|
|
if (message->FindInt32("buttons", &buttons) != B_OK
|
|
|| (buttons & B_PRIMARY_MOUSE_BUTTON) == 0)
|
|
return;
|
|
|
|
int32 index;
|
|
BRect workspaceFrame = _WorkspaceAt(where, index);
|
|
if (index < 0)
|
|
return;
|
|
|
|
Workspace workspace(*Window()->Desktop(), index);
|
|
workspaceFrame.InsetBy(1, 1);
|
|
|
|
BRect screenFrame = _ScreenFrame(index);
|
|
|
|
WindowLayer* window;
|
|
BRect windowFrame;
|
|
BPoint leftTop;
|
|
while (workspace.GetPreviousWindow(window, leftTop) == B_OK) {
|
|
BRect frame = _WindowFrame(workspaceFrame, screenFrame, window->Frame(),
|
|
leftTop);
|
|
if (frame.Contains(where) && window->Feel() != kDesktopWindowFeel
|
|
&& window->Feel() != kWindowScreenFeel) {
|
|
fSelectedWindow = window;
|
|
windowFrame = frame;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Some special functionality (clicked with modifiers)
|
|
|
|
int32 modifiers;
|
|
if (fSelectedWindow != NULL
|
|
&& message->FindInt32("modifiers", &modifiers) == B_OK) {
|
|
if ((modifiers & B_CONTROL_KEY) != 0) {
|
|
// Activate window if clicked with the control key pressed,
|
|
// minimize it if control+shift - this mirrors Deskbar
|
|
// shortcuts (when pressing a team menu item).
|
|
if ((modifiers & B_SHIFT_KEY) != 0)
|
|
fSelectedWindow->ServerWindow()->NotifyMinimize(true);
|
|
else
|
|
Window()->Desktop()->ActivateWindow(fSelectedWindow);
|
|
fSelectedWindow = NULL;
|
|
} else if ((modifiers & B_OPTION_KEY) != 0) {
|
|
// Also, send window to back if clicked with the option
|
|
// key pressed.
|
|
Window()->Desktop()->SendWindowBehind(fSelectedWindow);
|
|
fSelectedWindow = NULL;
|
|
}
|
|
}
|
|
|
|
// If this window is movable, we keep it selected
|
|
|
|
if (fSelectedWindow != NULL && (fSelectedWindow->Flags() & B_NOT_MOVABLE) != 0
|
|
|| fSelectedWindow == Window()) {
|
|
fSelectedWindow = NULL;
|
|
index = -1;
|
|
}
|
|
|
|
fLeftTopOffset = where - windowFrame.LeftTop();
|
|
fSelectedWorkspace = index;
|
|
|
|
if (index >= 0)
|
|
_Invalidate();
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::MouseUp(BMessage* message, BPoint where)
|
|
{
|
|
if (!fHasMoved && fSelectedWorkspace >= 0) {
|
|
int32 index;
|
|
_WorkspaceAt(where, index);
|
|
if (index >= 0)
|
|
Window()->Desktop()->SetWorkspace(index);
|
|
}
|
|
|
|
if (fSelectedWindow != NULL) {
|
|
// We need to hide the selection frame again
|
|
_Invalidate();
|
|
}
|
|
|
|
fSelectedWindow = NULL;
|
|
fSelectedWorkspace = -1;
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::MouseMoved(BMessage* message, BPoint where)
|
|
{
|
|
if (fSelectedWindow == NULL && fSelectedWorkspace < 0)
|
|
return;
|
|
|
|
// check if the correct mouse button is pressed
|
|
int32 buttons;
|
|
if (message->FindInt32("buttons", &buttons) != B_OK
|
|
|| (buttons & B_PRIMARY_MOUSE_BUTTON) == 0)
|
|
return;
|
|
|
|
if (!fHasMoved) {
|
|
Window()->Desktop()->SetMouseEventWindow(Window());
|
|
// don't let us off the mouse
|
|
}
|
|
|
|
int32 index;
|
|
BRect workspaceFrame = _WorkspaceAt(where, index);
|
|
|
|
if (fSelectedWindow == NULL) {
|
|
if (fSelectedWorkspace >= 0 && fSelectedWorkspace != index) {
|
|
fSelectedWorkspace = index;
|
|
_Invalidate();
|
|
}
|
|
return;
|
|
}
|
|
|
|
workspaceFrame.InsetBy(1, 1);
|
|
|
|
if (index != fSelectedWorkspace) {
|
|
if (!fSelectedWindow->InWorkspace(index) && fSelectedWindow->IsNormal()) {
|
|
// move window to this new workspace
|
|
uint32 newWorkspaces = fSelectedWindow->Workspaces()
|
|
& ~(1UL << fSelectedWorkspace) | (1UL << index);
|
|
|
|
Window()->Desktop()->SetWindowWorkspaces(fSelectedWindow,
|
|
newWorkspaces);
|
|
}
|
|
fSelectedWorkspace = index;
|
|
}
|
|
|
|
BRect screenFrame = _ScreenFrame(index);
|
|
float left = rintf((where.x - workspaceFrame.left - fLeftTopOffset.x)
|
|
* screenFrame.Width() / workspaceFrame.Width());
|
|
float top = rintf((where.y - workspaceFrame.top - fLeftTopOffset.y)
|
|
* screenFrame.Height() / workspaceFrame.Height());
|
|
|
|
BPoint leftTop;
|
|
if (fSelectedWorkspace == Window()->Desktop()->CurrentWorkspace())
|
|
leftTop = fSelectedWindow->Frame().LeftTop();
|
|
else {
|
|
if (fSelectedWindow->Anchor(fSelectedWorkspace).position == kInvalidWindowPosition)
|
|
fSelectedWindow->Anchor(fSelectedWorkspace).position = fSelectedWindow->Frame().LeftTop();
|
|
leftTop = fSelectedWindow->Anchor(fSelectedWorkspace).position;
|
|
}
|
|
|
|
Window()->Desktop()->MoveWindowBy(fSelectedWindow, left - leftTop.x, top - leftTop.y,
|
|
fSelectedWorkspace);
|
|
|
|
fHasMoved = true;
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::WindowChanged(WindowLayer* window)
|
|
{
|
|
// TODO: be smarter about this!
|
|
_Invalidate();
|
|
}
|
|
|
|
|
|
void
|
|
WorkspacesLayer::WindowRemoved(WindowLayer* window)
|
|
{
|
|
if (fSelectedWindow == window)
|
|
fSelectedWindow = NULL;
|
|
}
|
|
|