* Remove the bad designed GroupCookie class and move its functionality into the WindowArea. As an result each WindowArea only has one set of tabs and constraints.
* Fix group splitting. * Style: win -> parentWindow git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42610 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
d51bfbb57c
commit
e0bc3d9e2b
@ -22,6 +22,12 @@
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace LinearProgramming;
|
||||
|
||||
|
||||
const float kExtentPenalty = 1;
|
||||
const float kHighPenalty = 100;
|
||||
const float kInequalityPenalty = 10000;
|
||||
|
||||
|
||||
WindowArea::WindowArea(Crossing* leftTop, Crossing* rightTop,
|
||||
@ -32,33 +38,139 @@ WindowArea::WindowArea(Crossing* leftTop, Crossing* rightTop,
|
||||
fLeftTopCrossing(leftTop),
|
||||
fRightTopCrossing(rightTop),
|
||||
fLeftBottomCrossing(leftBottom),
|
||||
fRightBottomCrossing(rightBottom)
|
||||
fRightBottomCrossing(rightBottom),
|
||||
|
||||
fMinWidthConstraint(NULL),
|
||||
fMinHeightConstraint(NULL),
|
||||
fMaxWidthConstraint(NULL),
|
||||
fMaxHeightConstraint(NULL),
|
||||
fWidthConstraint(NULL),
|
||||
fHeightConstraint(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
WindowArea::SetGroup(SATGroup* group)
|
||||
{
|
||||
if (group && !group->fWindowAreaList.AddItem(this))
|
||||
return false;
|
||||
|
||||
if (fGroup)
|
||||
fGroup->fWindowAreaList.RemoveItem(this);
|
||||
|
||||
fGroup = group;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
WindowArea::~WindowArea()
|
||||
{
|
||||
if (fGroup)
|
||||
fGroup->WindowAreaRemoved(this);
|
||||
|
||||
_CleanupCorners();
|
||||
SetGroup(NULL);
|
||||
fGroup->fWindowAreaList.RemoveItem(this);
|
||||
|
||||
_UninitConstraints();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
WindowArea::Init(SATGroup* group)
|
||||
{
|
||||
_UninitConstraints();
|
||||
|
||||
if (group != NULL && group->fWindowAreaList.AddItem(this) == false)
|
||||
return false;
|
||||
|
||||
fGroup = group;
|
||||
|
||||
LinearSpec* linearSpec = fGroup->GetLinearSpec();
|
||||
|
||||
fMinWidthConstraint = linearSpec->AddConstraint(1.0, RightVar(), -1.0,
|
||||
LeftVar(), kGE, 0);
|
||||
fMinHeightConstraint = linearSpec->AddConstraint(1.0, BottomVar(), -1.0,
|
||||
TopVar(), kGE, 0);
|
||||
|
||||
fMaxWidthConstraint = linearSpec->AddConstraint(1.0, RightVar(), -1.0,
|
||||
LeftVar(), kLE, 0, kInequalityPenalty, kInequalityPenalty);
|
||||
fMaxHeightConstraint = linearSpec->AddConstraint(1.0, BottomVar(), -1.0,
|
||||
TopVar(), kLE, 0, kInequalityPenalty, kInequalityPenalty);
|
||||
|
||||
// Width and height have soft constraints
|
||||
fWidthConstraint = linearSpec->AddConstraint(1.0, RightVar(), -1.0,
|
||||
LeftVar(), kEQ, 0, kExtentPenalty,
|
||||
kExtentPenalty);
|
||||
fHeightConstraint = linearSpec->AddConstraint(-1.0, TopVar(), 1.0,
|
||||
BottomVar(), kEQ, 0, kExtentPenalty,
|
||||
kExtentPenalty);
|
||||
|
||||
if (!fMinWidthConstraint || !fMinHeightConstraint || !fWidthConstraint
|
||||
|| !fHeightConstraint || !fMaxWidthConstraint
|
||||
|| !fMaxHeightConstraint)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WindowArea::DoGroupLayout()
|
||||
{
|
||||
SATWindow* parentWindow = fWindowLayerOrder.ItemAt(0);
|
||||
if (parentWindow == NULL)
|
||||
return;
|
||||
|
||||
BRect frame = parentWindow->CompleteWindowFrame();
|
||||
// Make it also work for solver which don't support negative variables
|
||||
frame.OffsetBy(kMakePositiveOffset, kMakePositiveOffset);
|
||||
|
||||
// adjust window size soft constraints
|
||||
fWidthConstraint->SetRightSide(frame.Width());
|
||||
fHeightConstraint->SetRightSide(frame.Height());
|
||||
|
||||
LinearSpec* linearSpec = fGroup->GetLinearSpec();
|
||||
Constraint* leftConstraint = linearSpec->AddConstraint(1.0, LeftVar(),
|
||||
kEQ, frame.left);
|
||||
Constraint* topConstraint = linearSpec->AddConstraint(1.0, TopVar(), kEQ,
|
||||
frame.top);
|
||||
|
||||
// give soft constraints a high penalty
|
||||
fWidthConstraint->SetPenaltyNeg(kHighPenalty);
|
||||
fWidthConstraint->SetPenaltyPos(kHighPenalty);
|
||||
fHeightConstraint->SetPenaltyNeg(kHighPenalty);
|
||||
fHeightConstraint->SetPenaltyPos(kHighPenalty);
|
||||
|
||||
// After we set the new parameter solve and apply the new layout.
|
||||
ResultType result;
|
||||
for (int32 tries = 0; tries < 15; tries++) {
|
||||
result = fGroup->GetLinearSpec()->Solve();
|
||||
if (result == kInfeasible) {
|
||||
debug_printf("can't solve constraints!\n");
|
||||
break;
|
||||
}
|
||||
if (result == kOptimal) {
|
||||
const WindowAreaList& areas = fGroup->GetAreaList();
|
||||
for (int32 i = 0; i < areas.CountItems(); i++) {
|
||||
WindowArea* area = areas.ItemAt(i);
|
||||
area->_MoveToSAT(parentWindow);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// set penalties back to normal
|
||||
fWidthConstraint->SetPenaltyNeg(kExtentPenalty);
|
||||
fWidthConstraint->SetPenaltyPos(kExtentPenalty);
|
||||
fHeightConstraint->SetPenaltyNeg(kExtentPenalty);
|
||||
fHeightConstraint->SetPenaltyPos(kExtentPenalty);
|
||||
|
||||
linearSpec->RemoveConstraint(leftConstraint);
|
||||
linearSpec->RemoveConstraint(topConstraint);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WindowArea::UpdateSizeLimits()
|
||||
{
|
||||
_UpdateConstraintValues();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WindowArea::UpdateSizeConstaints(const BRect& frame)
|
||||
{
|
||||
// adjust window size soft constraints
|
||||
fWidthConstraint->SetRightSide(frame.Width());
|
||||
fHeightConstraint->SetRightSide(frame.Height());
|
||||
}
|
||||
|
||||
|
||||
@ -78,6 +190,46 @@ WindowArea::TopWindow()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WindowArea::_UpdateConstraintValues()
|
||||
{
|
||||
SATWindow* topWindow = TopWindow();
|
||||
if (topWindow == NULL)
|
||||
return;
|
||||
|
||||
int32 minWidth, maxWidth;
|
||||
int32 minHeight, maxHeight;
|
||||
SATWindow* window = fWindowList.ItemAt(0);
|
||||
window->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
|
||||
for (int32 i = 1; i < fWindowList.CountItems(); i++) {
|
||||
window = fWindowList.ItemAt(i);
|
||||
// size limit constraints
|
||||
int32 minW, maxW;
|
||||
int32 minH, maxH;
|
||||
window->GetSizeLimits(&minW, &maxW, &minH, &maxH);
|
||||
if (minWidth < minW)
|
||||
minWidth = minW;
|
||||
if (minHeight < minH)
|
||||
minHeight = minH;
|
||||
if (maxWidth < maxW)
|
||||
maxWidth = maxW;
|
||||
if (maxHeight < maxH)
|
||||
maxHeight = maxH;
|
||||
}
|
||||
|
||||
topWindow->AddDecorator(&minWidth, &maxWidth, &minHeight, &maxHeight);
|
||||
fMinWidthConstraint->SetRightSide(minWidth);
|
||||
fMinHeightConstraint->SetRightSide(minHeight);
|
||||
|
||||
fMaxWidthConstraint->SetRightSide(maxWidth);
|
||||
fMaxHeightConstraint->SetRightSide(maxHeight);
|
||||
|
||||
BRect frame = topWindow->CompleteWindowFrame();
|
||||
fWidthConstraint->SetRightSide(frame.Width());
|
||||
fHeightConstraint->SetRightSide(frame.Height());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
WindowArea::_AddWindow(SATWindow* window, SATWindow* after)
|
||||
{
|
||||
@ -94,6 +246,8 @@ WindowArea::_AddWindow(SATWindow* window, SATWindow* after)
|
||||
_InitCorners();
|
||||
|
||||
fWindowLayerOrder.AddItem(window);
|
||||
|
||||
_UpdateConstraintValues();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -105,6 +259,8 @@ WindowArea::_RemoveWindow(SATWindow* window)
|
||||
return false;
|
||||
|
||||
fWindowLayerOrder.RemoveItem(window);
|
||||
_UpdateConstraintValues();
|
||||
|
||||
window->RemovedFromArea(this);
|
||||
ReleaseReference();
|
||||
return true;
|
||||
@ -169,8 +325,24 @@ WindowArea::PropagateToGroup(SATGroup* group)
|
||||
fLeftBottomCrossing = newLeftBottom;
|
||||
fRightBottomCrossing = newRightBottom;
|
||||
|
||||
for (int i = 0; i < fWindowList.CountItems(); i++)
|
||||
fWindowList.ItemAt(i)->PropagateToGroup(group, this);
|
||||
_InitCorners();
|
||||
|
||||
BReference<SATGroup> oldGroup = fGroup;
|
||||
// manage constraints
|
||||
if (Init(group) == false)
|
||||
return false;
|
||||
oldGroup->fWindowAreaList.RemoveItem(this);
|
||||
for (int32 i = 0; i < fWindowList.CountItems(); i++) {
|
||||
SATWindow* window = fWindowList.ItemAt(i);
|
||||
if (oldGroup->fSATWindowList.RemoveItem(window) == false)
|
||||
return false;
|
||||
if (group->fSATWindowList.AddItem(window) == false) {
|
||||
_UninitConstraints();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_UpdateConstraintValues();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -185,6 +357,24 @@ WindowArea::MoveToTopLayer(SATWindow* window)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WindowArea::_UninitConstraints()
|
||||
{
|
||||
delete fMinWidthConstraint;
|
||||
delete fMinHeightConstraint;
|
||||
delete fMaxWidthConstraint;
|
||||
delete fMaxHeightConstraint;
|
||||
delete fWidthConstraint;
|
||||
delete fHeightConstraint;
|
||||
fMinWidthConstraint = NULL;
|
||||
fMinHeightConstraint = NULL;
|
||||
fMaxWidthConstraint = NULL;
|
||||
fMaxHeightConstraint = NULL;
|
||||
fWidthConstraint = NULL;
|
||||
fHeightConstraint = NULL;
|
||||
}
|
||||
|
||||
|
||||
BReference<Crossing>
|
||||
WindowArea::_CrossingByPosition(Crossing* crossing, SATGroup* group)
|
||||
{
|
||||
@ -198,7 +388,7 @@ WindowArea::_CrossingByPosition(Crossing* crossing, SATGroup* group)
|
||||
return crossRef;
|
||||
|
||||
Tab* oldVTab = crossing->VerticalTab();
|
||||
crossRef = hTab->FindCrossing(oldHTab->Position());
|
||||
crossRef = hTab->FindCrossing(oldVTab->Position());
|
||||
if (crossRef)
|
||||
return crossRef;
|
||||
|
||||
@ -294,6 +484,38 @@ WindowArea::_UnsetNeighbourCorner(Corner* neighbour, Corner* opponent)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WindowArea::_MoveToSAT(SATWindow* topWindow)
|
||||
{
|
||||
int32 workspace = topWindow->GetWindow()->CurrentWorkspace();
|
||||
Desktop* desktop = topWindow->GetWindow()->Desktop();
|
||||
|
||||
BRect frameSAT(LeftVar()->Value() - kMakePositiveOffset,
|
||||
TopVar()->Value() - kMakePositiveOffset,
|
||||
RightVar()->Value() - kMakePositiveOffset,
|
||||
BottomVar()->Value() - kMakePositiveOffset);
|
||||
|
||||
for (int32 i = 0; i < fWindowList.CountItems(); i++) {
|
||||
SATWindow* window = fWindowList.ItemAt(i);
|
||||
window->AdjustSizeLimits(frameSAT);
|
||||
|
||||
BRect frame = window->CompleteWindowFrame();
|
||||
float deltaToX = round(frameSAT.left - frame.left);
|
||||
float deltaToY = round(frameSAT.top - frame.top);
|
||||
frame.OffsetBy(deltaToX, deltaToY);
|
||||
float deltaByX = round(frameSAT.right - frame.right);
|
||||
float deltaByY = round(frameSAT.bottom - frame.bottom);
|
||||
|
||||
desktop->MoveWindowBy(window->GetWindow(), deltaToX, deltaToY,
|
||||
workspace);
|
||||
// Update frame to the new position
|
||||
desktop->ResizeWindowBy(window->GetWindow(), deltaByX, deltaByY);
|
||||
}
|
||||
|
||||
UpdateSizeConstaints(frameSAT);
|
||||
}
|
||||
|
||||
|
||||
Corner::Corner()
|
||||
:
|
||||
status(kNotDockable),
|
||||
@ -308,21 +530,21 @@ Corner::Trace() const
|
||||
{
|
||||
switch (status) {
|
||||
case kFree:
|
||||
STRACE_SAT("free corner\n");
|
||||
debug_printf("free corner\n");
|
||||
break;
|
||||
|
||||
case kUsed:
|
||||
{
|
||||
STRACE_SAT("attached windows:\n");
|
||||
debug_printf("attached windows:\n");
|
||||
const SATWindowList& list = windowArea->WindowList();
|
||||
for (int i = 0; i < list.CountItems(); i++) {
|
||||
STRACE_SAT("- %s\n", list.ItemAt(i)->GetWindow()->Title());
|
||||
debug_printf("- %s\n", list.ItemAt(i)->GetWindow()->Title());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kNotDockable:
|
||||
STRACE_SAT("not dockable\n");
|
||||
debug_printf("not dockable\n");
|
||||
break;
|
||||
};
|
||||
}
|
||||
@ -333,8 +555,6 @@ Crossing::Crossing(Tab* vertical, Tab* horizontal)
|
||||
fVerticalTab(vertical),
|
||||
fHorizontalTab(horizontal)
|
||||
{
|
||||
fVerticalTab->AcquireReference();
|
||||
fHorizontalTab->AcquireReference();
|
||||
}
|
||||
|
||||
|
||||
@ -342,9 +562,6 @@ Crossing::~Crossing()
|
||||
{
|
||||
fVerticalTab->RemoveCrossing(this);
|
||||
fHorizontalTab->RemoveCrossing(this);
|
||||
|
||||
fVerticalTab->ReleaseReference();
|
||||
fHorizontalTab->ReleaseReference();
|
||||
}
|
||||
|
||||
|
||||
@ -379,13 +596,13 @@ Crossing::HorizontalTab() const
|
||||
void
|
||||
Crossing::Trace() const
|
||||
{
|
||||
STRACE_SAT("left-top corner: ");
|
||||
debug_printf("left-top corner: ");
|
||||
fCorners[Corner::kLeftTop].Trace();
|
||||
STRACE_SAT("right-top corner: ");
|
||||
debug_printf("right-top corner: ");
|
||||
fCorners[Corner::kRightTop].Trace();
|
||||
STRACE_SAT("left-bottom corner: ");
|
||||
debug_printf("left-bottom corner: ");
|
||||
fCorners[Corner::kLeftBottom].Trace();
|
||||
STRACE_SAT("right-bottom corner: ");
|
||||
debug_printf("right-bottom corner: ");
|
||||
fCorners[Corner::kRightBottom].Trace();
|
||||
}
|
||||
|
||||
@ -502,12 +719,14 @@ Tab::FindCrossingIndex(float pos)
|
||||
{
|
||||
if (fOrientation == kVertical) {
|
||||
for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
|
||||
if (fCrossingList.ItemAt(i)->HorizontalTab()->Position() == pos)
|
||||
if (fabs(fCrossingList.ItemAt(i)->HorizontalTab()->Position() - pos)
|
||||
< 0.0001)
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
|
||||
if (fCrossingList.ItemAt(i)->VerticalTab()->Position() == pos)
|
||||
if (fabs(fCrossingList.ItemAt(i)->VerticalTab()->Position() - pos)
|
||||
< 0.0001)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -557,7 +776,6 @@ SATGroup::SATGroup()
|
||||
|
||||
SATGroup::~SATGroup()
|
||||
{
|
||||
ASSERT(fSATWindowList.CountItems() == 0);
|
||||
// Should be empty
|
||||
//while (fSATWindowList.CountItems() > 0)
|
||||
// RemoveWindow(fSATWindowList.ItemAt(0));
|
||||
@ -628,7 +846,7 @@ SATGroup::AddWindow(SATWindow* window, Tab* left, Tab* top, Tab* right,
|
||||
if (!area)
|
||||
return false;
|
||||
// the area register itself in our area list
|
||||
if (!area->SetGroup(this)) {
|
||||
if (area->Init(this) == false) {
|
||||
delete area;
|
||||
return false;
|
||||
}
|
||||
@ -733,18 +951,6 @@ SATGroup::FindVerticalTab(float position)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SATGroup::AdjustWindows(SATWindow* triggerWindow)
|
||||
{
|
||||
// set window locations and sizes
|
||||
for (int i = 0; i < fSATWindowList.CountItems(); i++) {
|
||||
SATWindow* windowSAT = fSATWindowList.ItemAt(i);
|
||||
windowSAT->MoveWindowToSAT(
|
||||
triggerWindow->GetWindow()->CurrentWorkspace());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SATGroup::WindowAreaRemoved(WindowArea* area)
|
||||
{
|
||||
@ -927,7 +1133,7 @@ Tab*
|
||||
SATGroup::_FindTab(const TabList& list, float position)
|
||||
{
|
||||
for (int i = 0; i < list.CountItems(); i++)
|
||||
if (list.ItemAt(i)->Position() == position)
|
||||
if (fabs(list.ItemAt(i)->Position() - position) < 0.00001)
|
||||
return list.ItemAt(i);
|
||||
|
||||
return NULL;
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "ObjectList.h"
|
||||
#include "Referenceable.h"
|
||||
|
||||
#include "MagneticBorder.h"
|
||||
|
||||
#include "LinearSpec.h"
|
||||
|
||||
|
||||
@ -72,10 +74,10 @@ public:
|
||||
|
||||
void Trace() const;
|
||||
private:
|
||||
Corner fCorners[4];
|
||||
Corner fCorners[4];
|
||||
|
||||
Tab* fVerticalTab;
|
||||
Tab* fHorizontalTab;
|
||||
BReference<Tab> fVerticalTab;
|
||||
BReference<Tab> fHorizontalTab;
|
||||
};
|
||||
|
||||
|
||||
@ -137,7 +139,12 @@ public:
|
||||
Crossing* rightBottom);
|
||||
~WindowArea();
|
||||
|
||||
bool SetGroup(SATGroup* group);
|
||||
bool Init(SATGroup* group);
|
||||
SATGroup* Group() { return fGroup; }
|
||||
|
||||
void DoGroupLayout();
|
||||
void UpdateSizeLimits();
|
||||
void UpdateSizeConstaints(const BRect& frame);
|
||||
|
||||
const SATWindowList& WindowList() { return fWindowList; }
|
||||
const SATWindowList& LayerOrder() { return fWindowLayerOrder; }
|
||||
@ -159,6 +166,11 @@ public:
|
||||
Tab* TopTab();
|
||||
Tab* BottomTab();
|
||||
|
||||
Variable* LeftVar() { return LeftTab()->Var(); }
|
||||
Variable* RightVar() { return RightTab()->Var(); }
|
||||
Variable* TopVar() { return TopTab()->Var(); }
|
||||
Variable* BottomVar() { return BottomTab()->Var(); }
|
||||
|
||||
BRect Frame();
|
||||
|
||||
bool PropagateToGroup(SATGroup* group);
|
||||
@ -167,6 +179,9 @@ public:
|
||||
|
||||
private:
|
||||
friend class SATGroup;
|
||||
void _UninitConstraints();
|
||||
void _UpdateConstraintValues();
|
||||
|
||||
/*! SATGroup adds new windows to the area. */
|
||||
bool _AddWindow(SATWindow* window,
|
||||
SATWindow* after = NULL);
|
||||
@ -188,7 +203,9 @@ private:
|
||||
BReference<Crossing> _CrossingByPosition(Crossing* crossing,
|
||||
SATGroup* group);
|
||||
|
||||
SATGroup* fGroup;
|
||||
void _MoveToSAT(SATWindow* topWindow);
|
||||
|
||||
BReference<SATGroup> fGroup;
|
||||
|
||||
SATWindowList fWindowList;
|
||||
|
||||
@ -198,6 +215,17 @@ private:
|
||||
BReference<Crossing> fRightTopCrossing;
|
||||
BReference<Crossing> fLeftBottomCrossing;
|
||||
BReference<Crossing> fRightBottomCrossing;
|
||||
|
||||
Constraint* fMinWidthConstraint;
|
||||
Constraint* fMinHeightConstraint;
|
||||
Constraint* fMaxWidthConstraint;
|
||||
Constraint* fMaxHeightConstraint;
|
||||
Constraint* fKeepMaxWidthConstraint;
|
||||
Constraint* fKeepMaxHeightConstraint;
|
||||
Constraint* fWidthConstraint;
|
||||
Constraint* fHeightConstraint;
|
||||
|
||||
MagneticBorder fMagneticBorder;
|
||||
};
|
||||
|
||||
|
||||
@ -219,8 +247,6 @@ public:
|
||||
|
||||
LinearSpec* GetLinearSpec() { return &fLinearSpec; }
|
||||
|
||||
void AdjustWindows(SATWindow* triggerWindow);
|
||||
|
||||
/*! Create a new WindowArea from the crossing and add the window. */
|
||||
bool AddWindow(SATWindow* window, Tab* left, Tab* top,
|
||||
Tab* right, Tab* bottom);
|
||||
|
@ -20,235 +20,6 @@
|
||||
|
||||
|
||||
using namespace BPrivate;
|
||||
using namespace LinearProgramming;
|
||||
|
||||
|
||||
const float kExtentPenalty = 1;
|
||||
const float kHighPenalty = 10;
|
||||
const float kInequalityPenalty = 10000;
|
||||
|
||||
|
||||
GroupCookie::GroupCookie(SATWindow* satWindow)
|
||||
:
|
||||
fSATWindow(satWindow),
|
||||
|
||||
fWindowArea(NULL),
|
||||
|
||||
fLeftBorder(NULL),
|
||||
fTopBorder(NULL),
|
||||
fRightBorder(NULL),
|
||||
fBottomBorder(NULL),
|
||||
|
||||
fMinWidthConstraint(NULL),
|
||||
fMinHeightConstraint(NULL),
|
||||
fMaxWidthConstraint(NULL),
|
||||
fMaxHeightConstraint(NULL),
|
||||
fWidthConstraint(NULL),
|
||||
fHeightConstraint(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
GroupCookie::~GroupCookie()
|
||||
{
|
||||
Uninit();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GroupCookie::DoGroupLayout()
|
||||
{
|
||||
if (!fSATGroup.Get())
|
||||
return;
|
||||
|
||||
BRect frame = fSATWindow->CompleteWindowFrame();
|
||||
// Make it also work for solver which don't support negative variables
|
||||
frame.OffsetBy(kMakePositiveOffset, kMakePositiveOffset);
|
||||
|
||||
// adjust window size soft constraints
|
||||
fWidthConstraint->SetRightSide(frame.Width());
|
||||
fHeightConstraint->SetRightSide(frame.Height());
|
||||
|
||||
LinearSpec* linearSpec = fSATGroup->GetLinearSpec();
|
||||
Constraint* leftConstraint = linearSpec->AddConstraint(1.0, fLeftBorder,
|
||||
kEQ, frame.left);
|
||||
Constraint* topConstraint = linearSpec->AddConstraint(1.0, fTopBorder, kEQ,
|
||||
frame.top);
|
||||
|
||||
// give soft constraints a high penalty
|
||||
fWidthConstraint->SetPenaltyNeg(kHighPenalty);
|
||||
fWidthConstraint->SetPenaltyPos(kHighPenalty);
|
||||
fHeightConstraint->SetPenaltyNeg(kHighPenalty);
|
||||
fHeightConstraint->SetPenaltyPos(kHighPenalty);
|
||||
|
||||
// After we set the new parameter solve and apply the new layout.
|
||||
ResultType result;
|
||||
for (int32 tries = 0; tries < 15; tries++) {
|
||||
result = fSATGroup->GetLinearSpec()->Solve();
|
||||
if (result == kInfeasible) {
|
||||
debug_printf("can't solve constraints!\n");
|
||||
break;
|
||||
}
|
||||
if (result == kOptimal) {
|
||||
fSATGroup->AdjustWindows(fSATWindow);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// set penalties back to normal
|
||||
fWidthConstraint->SetPenaltyNeg(kExtentPenalty);
|
||||
fWidthConstraint->SetPenaltyPos(kExtentPenalty);
|
||||
fHeightConstraint->SetPenaltyNeg(kExtentPenalty);
|
||||
fHeightConstraint->SetPenaltyPos(kExtentPenalty);
|
||||
|
||||
linearSpec->RemoveConstraint(leftConstraint);
|
||||
linearSpec->RemoveConstraint(topConstraint);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GroupCookie::MoveWindow(int32 workspace)
|
||||
{
|
||||
Window* window = fSATWindow->GetWindow();
|
||||
Desktop* desktop = window->Desktop();
|
||||
|
||||
BRect frame = fSATWindow->CompleteWindowFrame();
|
||||
BRect frameSAT(fLeftBorder->Value() - kMakePositiveOffset,
|
||||
fTopBorder->Value() - kMakePositiveOffset,
|
||||
fRightBorder->Value() - kMakePositiveOffset,
|
||||
fBottomBorder->Value() - kMakePositiveOffset);
|
||||
|
||||
fSATWindow->AdjustSizeLimits(frameSAT);
|
||||
desktop->MoveWindowBy(window, round(frameSAT.left - frame.left),
|
||||
round(frameSAT.top - frame.top), workspace);
|
||||
|
||||
// Update frame to the new position
|
||||
frame.OffsetBy(round(frameSAT.left - frame.left),
|
||||
round(frameSAT.top - frame.top));
|
||||
desktop->ResizeWindowBy(window, round(frameSAT.right - frame.right),
|
||||
round(frameSAT.bottom - frame.bottom));
|
||||
|
||||
UpdateSizeConstaints(frameSAT);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GroupCookie::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight,
|
||||
int32 maxHeight)
|
||||
{
|
||||
fMinWidthConstraint->SetRightSide(minWidth);
|
||||
fMinHeightConstraint->SetRightSide(minHeight);
|
||||
fMaxWidthConstraint->SetRightSide(maxWidth);
|
||||
fMaxHeightConstraint->SetRightSide(maxHeight);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GroupCookie::UpdateSizeConstaints(const BRect& frame)
|
||||
{
|
||||
// adjust window size soft constraints
|
||||
if (fSATWindow->IsHResizeable() == true)
|
||||
fWidthConstraint->SetRightSide(frame.Width());
|
||||
if (fSATWindow->IsVResizeable() == true)
|
||||
fHeightConstraint->SetRightSide(frame.Height());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
GroupCookie::Init(SATGroup* group, WindowArea* area)
|
||||
{
|
||||
ASSERT(fSATGroup.Get() == NULL);
|
||||
|
||||
fSATGroup.SetTo(group);
|
||||
fWindowArea = area;
|
||||
|
||||
LinearSpec* linearSpec = group->GetLinearSpec();
|
||||
// create variables
|
||||
fLeftBorder = area->LeftTab()->Var();
|
||||
fTopBorder = area->TopTab()->Var();
|
||||
fRightBorder = area->RightTab()->Var();
|
||||
fBottomBorder = area->BottomTab()->Var();
|
||||
|
||||
// size limit constraints
|
||||
int32 minWidth, maxWidth;
|
||||
int32 minHeight, maxHeight;
|
||||
fSATWindow->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
|
||||
fSATWindow->AddDecorator(&minWidth, &maxWidth, &minHeight, &maxHeight);
|
||||
|
||||
fMinWidthConstraint = linearSpec->AddConstraint(1.0, fRightBorder, -1.0,
|
||||
fLeftBorder, kGE, minWidth);
|
||||
fMinHeightConstraint = linearSpec->AddConstraint(1.0, fBottomBorder, -1.0,
|
||||
fTopBorder, kGE, minHeight);
|
||||
|
||||
fMaxWidthConstraint = linearSpec->AddConstraint(1.0, fRightBorder, -1.0,
|
||||
fLeftBorder, kLE, maxWidth, kInequalityPenalty, kInequalityPenalty);
|
||||
fMaxHeightConstraint = linearSpec->AddConstraint(1.0, fBottomBorder, -1.0,
|
||||
fTopBorder, kLE, maxHeight, kInequalityPenalty, kInequalityPenalty);
|
||||
|
||||
// Width and height have soft constraints
|
||||
BRect frame = fSATWindow->CompleteWindowFrame();
|
||||
fWidthConstraint = linearSpec->AddConstraint(1.0, fRightBorder, -1.0,
|
||||
fLeftBorder, kEQ, frame.Width(), kExtentPenalty,
|
||||
kExtentPenalty);
|
||||
fHeightConstraint = linearSpec->AddConstraint(-1.0, fTopBorder, 1.0,
|
||||
fBottomBorder, kEQ, frame.Height(), kExtentPenalty,
|
||||
kExtentPenalty);
|
||||
|
||||
if (!fMinWidthConstraint || !fMinHeightConstraint || !fWidthConstraint
|
||||
|| !fHeightConstraint || !fMaxWidthConstraint
|
||||
|| !fMaxHeightConstraint) {
|
||||
// clean up
|
||||
Uninit();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GroupCookie::Uninit()
|
||||
{
|
||||
fLeftBorder = NULL;
|
||||
fTopBorder = NULL;
|
||||
fRightBorder = NULL;
|
||||
fBottomBorder = NULL;
|
||||
|
||||
delete fMinWidthConstraint;
|
||||
delete fMinHeightConstraint;
|
||||
delete fMaxWidthConstraint;
|
||||
delete fMaxHeightConstraint;
|
||||
delete fWidthConstraint;
|
||||
delete fHeightConstraint;
|
||||
fMinWidthConstraint = NULL;
|
||||
fMinHeightConstraint = NULL;
|
||||
fMaxWidthConstraint = NULL;
|
||||
fMaxHeightConstraint = NULL;
|
||||
fWidthConstraint = NULL;
|
||||
fHeightConstraint = NULL;
|
||||
|
||||
fSATGroup.Unset();
|
||||
fWindowArea = NULL;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
GroupCookie::PropagateToGroup(SATGroup* group, WindowArea* area)
|
||||
{
|
||||
if (!fSATGroup->fSATWindowList.RemoveItem(fSATWindow))
|
||||
return false;
|
||||
Uninit();
|
||||
|
||||
if (!Init(group, area))
|
||||
return false;
|
||||
|
||||
if (!area->SetGroup(group) || !group->fSATWindowList.AddItem(fSATWindow)) {
|
||||
Uninit();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
@ -259,13 +30,11 @@ SATWindow::SATWindow(StackAndTile* sat, Window* window)
|
||||
fWindow(window),
|
||||
fStackAndTile(sat),
|
||||
|
||||
fOwnGroupCookie(this),
|
||||
fForeignGroupCookie(this),
|
||||
fWindowArea(NULL),
|
||||
|
||||
fOngoingSnapping(NULL),
|
||||
fSATStacking(this),
|
||||
fSATTiling(this),
|
||||
fShutdown(false)
|
||||
fSATTiling(this)
|
||||
{
|
||||
fId = _GenerateId();
|
||||
|
||||
@ -278,9 +47,6 @@ SATWindow::SATWindow(StackAndTile* sat, Window* window)
|
||||
fOriginalWidth = frame.Width();
|
||||
fOriginalHeight = frame.Height();
|
||||
|
||||
fGroupCookie = &fOwnGroupCookie;
|
||||
_InitGroup();
|
||||
|
||||
fSATSnappingBehaviourList.AddItem(&fSATStacking);
|
||||
fSATSnappingBehaviourList.AddItem(&fSATTiling);
|
||||
}
|
||||
@ -288,12 +54,8 @@ SATWindow::SATWindow(StackAndTile* sat, Window* window)
|
||||
|
||||
SATWindow::~SATWindow()
|
||||
{
|
||||
fShutdown = true;
|
||||
|
||||
if (fForeignGroupCookie.GetGroup())
|
||||
fForeignGroupCookie.GetGroup()->RemoveWindow(this);
|
||||
if (fOwnGroupCookie.GetGroup())
|
||||
fOwnGroupCookie.GetGroup()->RemoveWindow(this);
|
||||
if (fWindowArea != NULL)
|
||||
fWindowArea->Group()->RemoveWindow(this);
|
||||
}
|
||||
|
||||
|
||||
@ -307,22 +69,33 @@ SATWindow::GetDecorator() const
|
||||
SATGroup*
|
||||
SATWindow::GetGroup()
|
||||
{
|
||||
if (!fGroupCookie->GetGroup())
|
||||
_InitGroup();
|
||||
if (fWindowArea == NULL) {
|
||||
SATGroup* group = new (std::nothrow)SATGroup;
|
||||
if (group == NULL)
|
||||
return group;
|
||||
BReference<SATGroup> groupRef;
|
||||
groupRef.SetTo(group, true);
|
||||
|
||||
// manually set the tabs of the single window
|
||||
WindowArea* windowArea = fGroupCookie->GetWindowArea();
|
||||
if (!PositionManagedBySAT() && windowArea) {
|
||||
/* AddWindow also will trigger the window to hold a reference on the new
|
||||
group. */
|
||||
if (group->AddWindow(this, NULL, NULL, NULL, NULL) == false)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ASSERT(fWindowArea != NULL);
|
||||
|
||||
// manually set the tabs of the single window
|
||||
if (PositionManagedBySAT() == false) {
|
||||
BRect frame = CompleteWindowFrame();
|
||||
windowArea->LeftTopCrossing()->VerticalTab()->SetPosition(frame.left);
|
||||
windowArea->LeftTopCrossing()->HorizontalTab()->SetPosition(frame.top);
|
||||
windowArea->RightBottomCrossing()->VerticalTab()->SetPosition(
|
||||
fWindowArea->LeftTopCrossing()->VerticalTab()->SetPosition(frame.left);
|
||||
fWindowArea->LeftTopCrossing()->HorizontalTab()->SetPosition(frame.top);
|
||||
fWindowArea->RightBottomCrossing()->VerticalTab()->SetPosition(
|
||||
frame.right);
|
||||
windowArea->RightBottomCrossing()->HorizontalTab()->SetPosition(
|
||||
fWindowArea->RightBottomCrossing()->HorizontalTab()->SetPosition(
|
||||
frame.bottom);
|
||||
}
|
||||
|
||||
return fGroupCookie->GetGroup();
|
||||
return fWindowArea->Group();
|
||||
}
|
||||
|
||||
|
||||
@ -340,16 +113,11 @@ SATWindow::HandleMessage(SATWindow* sender, BPrivate::LinkReceiver& link,
|
||||
|
||||
|
||||
bool
|
||||
SATWindow::PropagateToGroup(SATGroup* group, WindowArea* area)
|
||||
SATWindow::PropagateToGroup(SATGroup* group)
|
||||
{
|
||||
return fGroupCookie->PropagateToGroup(group, area);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SATWindow::MoveWindowToSAT(int32 workspace)
|
||||
{
|
||||
fGroupCookie->MoveWindow(workspace);
|
||||
if (fWindowArea == NULL)
|
||||
return false;
|
||||
return fWindowArea->PropagateToGroup(group);
|
||||
}
|
||||
|
||||
|
||||
@ -358,16 +126,7 @@ SATWindow::AddedToGroup(SATGroup* group, WindowArea* area)
|
||||
{
|
||||
STRACE_SAT("SATWindow::AddedToGroup group: %p window %s\n", group,
|
||||
fWindow->Title());
|
||||
if (fGroupCookie == &fForeignGroupCookie)
|
||||
return false;
|
||||
if (fOwnGroupCookie.GetGroup())
|
||||
fGroupCookie = &fForeignGroupCookie;
|
||||
|
||||
if (!fGroupCookie->Init(group, area)) {
|
||||
fGroupCookie = &fOwnGroupCookie;
|
||||
return false;
|
||||
}
|
||||
|
||||
fWindowArea = area;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -382,18 +141,7 @@ SATWindow::RemovedFromGroup(SATGroup* group, bool stayBelowMouse)
|
||||
if (group->CountItems() == 1)
|
||||
group->WindowAt(0)->_RestoreOriginalSize(false);
|
||||
|
||||
if (fShutdown) {
|
||||
fGroupCookie->Uninit();
|
||||
return true;
|
||||
}
|
||||
|
||||
ASSERT(fGroupCookie->GetGroup() == group);
|
||||
fGroupCookie->Uninit();
|
||||
if (fGroupCookie == &fOwnGroupCookie)
|
||||
_InitGroup();
|
||||
else
|
||||
fGroupCookie = &fOwnGroupCookie;
|
||||
|
||||
fWindowArea = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -485,7 +233,8 @@ SATWindow::DoGroupLayout()
|
||||
if (!PositionManagedBySAT())
|
||||
return;
|
||||
|
||||
fGroupCookie->DoGroupLayout();
|
||||
if (fWindowArea != NULL)
|
||||
fWindowArea->DoGroupLayout();
|
||||
}
|
||||
|
||||
|
||||
@ -584,9 +333,8 @@ SATWindow::SetOriginalSizeLimits(int32 minWidth, int32 maxWidth,
|
||||
fOriginalMinHeight = minHeight;
|
||||
fOriginalMaxHeight = maxHeight;
|
||||
|
||||
GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
|
||||
AddDecorator(&minWidth, &maxWidth, &minHeight, &maxHeight);
|
||||
fGroupCookie->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
|
||||
if (fWindowArea != NULL)
|
||||
fWindowArea->UpdateSizeLimits();
|
||||
}
|
||||
|
||||
|
||||
@ -604,7 +352,8 @@ SATWindow::Resized()
|
||||
if (vResizeable)
|
||||
fOriginalHeight = frame.Height();
|
||||
|
||||
fGroupCookie->UpdateSizeConstaints(CompleteWindowFrame());
|
||||
if (fWindowArea != NULL)
|
||||
fWindowArea->UpdateSizeConstaints(CompleteWindowFrame());
|
||||
}
|
||||
|
||||
|
||||
@ -653,7 +402,7 @@ SATWindow::CompleteWindowFrame()
|
||||
bool
|
||||
SATWindow::PositionManagedBySAT()
|
||||
{
|
||||
if (fGroupCookie->GetGroup() && fGroupCookie->GetGroup()->CountItems() == 1)
|
||||
if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -723,25 +472,6 @@ SATWindow::GetSettings(BMessage& message)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SATWindow::_InitGroup()
|
||||
{
|
||||
ASSERT(fGroupCookie == &fOwnGroupCookie);
|
||||
ASSERT(fOwnGroupCookie.GetGroup() == NULL);
|
||||
STRACE_SAT("SATWindow::_InitGroup %s\n", fWindow->Title());
|
||||
SATGroup* group = new (std::nothrow)SATGroup;
|
||||
if (!group)
|
||||
return;
|
||||
BReference<SATGroup> groupRef;
|
||||
groupRef.SetTo(group, true);
|
||||
|
||||
/* AddWindow also will trigger the window to hold a reference on the new
|
||||
group. */
|
||||
if (!groupRef->AddWindow(this, NULL, NULL, NULL, NULL))
|
||||
STRACE_SAT("SATWindow::_InitGroup(): adding window to group failed\n");
|
||||
}
|
||||
|
||||
|
||||
uint64
|
||||
SATWindow::_GenerateId()
|
||||
{
|
||||
|
@ -11,8 +11,8 @@
|
||||
|
||||
#include <Region.h>
|
||||
|
||||
#include "MagneticBorder.h"
|
||||
#include "SATDecorator.h"
|
||||
#include "SATGroup.h"
|
||||
#include "Stacking.h"
|
||||
#include "Tiling.h"
|
||||
|
||||
@ -23,53 +23,6 @@ class StackAndTile;
|
||||
class Window;
|
||||
|
||||
|
||||
class GroupCookie
|
||||
{
|
||||
public:
|
||||
GroupCookie(SATWindow* satWindow);
|
||||
~GroupCookie();
|
||||
|
||||
bool Init(SATGroup* group, WindowArea* area);
|
||||
void Uninit();
|
||||
|
||||
void DoGroupLayout();
|
||||
void MoveWindow(int32 workspace);
|
||||
void SetSizeLimits(int32 minWidth, int32 maxWidth,
|
||||
int32 minHeight, int32 maxHeight);
|
||||
void UpdateSizeConstaints(const BRect& frame);
|
||||
|
||||
SATGroup* GetGroup() { return fSATGroup.Get(); }
|
||||
|
||||
WindowArea* GetWindowArea() { return fWindowArea; }
|
||||
|
||||
bool PropagateToGroup(SATGroup* group,
|
||||
WindowArea* area);
|
||||
|
||||
private:
|
||||
SATWindow* fSATWindow;
|
||||
|
||||
BReference<SATGroup> fSATGroup;
|
||||
|
||||
WindowArea* fWindowArea;
|
||||
|
||||
Variable* fLeftBorder;
|
||||
Variable* fTopBorder;
|
||||
Variable* fRightBorder;
|
||||
Variable* fBottomBorder;
|
||||
|
||||
Constraint* fMinWidthConstraint;
|
||||
Constraint* fMinHeightConstraint;
|
||||
Constraint* fMaxWidthConstraint;
|
||||
Constraint* fMaxHeightConstraint;
|
||||
Constraint* fKeepMaxWidthConstraint;
|
||||
Constraint* fKeepMaxHeightConstraint;
|
||||
Constraint* fWidthConstraint;
|
||||
Constraint* fHeightConstraint;
|
||||
|
||||
MagneticBorder fMagneticBorder;
|
||||
};
|
||||
|
||||
|
||||
class SATWindow {
|
||||
public:
|
||||
SATWindow(StackAndTile* sat, Window* window);
|
||||
@ -81,18 +34,13 @@ public:
|
||||
Desktop* GetDesktop() { return fDesktop; }
|
||||
//! Can be NULL if memory allocation failed!
|
||||
SATGroup* GetGroup();
|
||||
WindowArea* GetWindowArea() {
|
||||
return fGroupCookie->GetWindowArea(); }
|
||||
WindowArea* GetWindowArea() { return fWindowArea; }
|
||||
|
||||
bool HandleMessage(SATWindow* sender,
|
||||
BPrivate::LinkReceiver& link,
|
||||
BPrivate::LinkSender& reply);
|
||||
|
||||
bool PropagateToGroup(SATGroup* group,
|
||||
WindowArea* area);
|
||||
|
||||
//! Move the window to the tab's position.
|
||||
void MoveWindowToSAT(int32 workspace);
|
||||
bool PropagateToGroup(SATGroup* group);
|
||||
|
||||
// hook function called from SATGroup
|
||||
bool AddedToGroup(SATGroup* group, WindowArea* area);
|
||||
@ -139,7 +87,6 @@ public:
|
||||
bool SetSettings(const BMessage& message);
|
||||
void GetSettings(BMessage& message);
|
||||
private:
|
||||
void _InitGroup();
|
||||
uint64 _GenerateId();
|
||||
|
||||
void _UpdateSizeLimits();
|
||||
@ -151,11 +98,7 @@ private:
|
||||
Desktop* fDesktop;
|
||||
|
||||
//! Current group.
|
||||
GroupCookie* fGroupCookie;
|
||||
/*! If the window is added to another group the own group is cached
|
||||
here. */
|
||||
GroupCookie fOwnGroupCookie;
|
||||
GroupCookie fForeignGroupCookie;
|
||||
WindowArea* fWindowArea;
|
||||
|
||||
SATSnappingBehaviour* fOngoingSnapping;
|
||||
SATStacking fSATStacking;
|
||||
@ -163,8 +106,6 @@ private:
|
||||
|
||||
SATSnappingBehaviourList fSATSnappingBehaviourList;
|
||||
|
||||
bool fShutdown;
|
||||
|
||||
int32 fOriginalMinWidth;
|
||||
int32 fOriginalMaxWidth;
|
||||
int32 fOriginalMinHeight;
|
||||
|
@ -237,13 +237,14 @@ SATStacking::FindSnappingCandidates(SATGroup* group)
|
||||
for (int i = 0; i < group->CountItems(); i++) {
|
||||
SATWindow* satWindow = group->WindowAt(i);
|
||||
// search for stacking parent
|
||||
Window* win = satWindow->GetWindow();
|
||||
if (win == window || !win->Decorator())
|
||||
Window* parentWindow = satWindow->GetWindow();
|
||||
if (parentWindow == window || parentWindow->Decorator() == NULL)
|
||||
continue;
|
||||
if (_IsStackableWindow(win) == false
|
||||
if (_IsStackableWindow(parentWindow) == false
|
||||
|| _IsStackableWindow(window) == false)
|
||||
continue;
|
||||
Decorator::Tab* tab = win->Decorator()->TabAt(win->PositionInStack());
|
||||
Decorator::Tab* tab = parentWindow->Decorator()->TabAt(
|
||||
parentWindow->PositionInStack());
|
||||
if (tab == NULL)
|
||||
continue;
|
||||
if (tab->tabRect.Contains(mousePosition)) {
|
||||
|
Loading…
Reference in New Issue
Block a user