19e179ca4f
window of the view into the application thread. This solves the race condition with asynchronous SetViewCursor and deleting the cursor immediately afterwards for real. * The ServerApp now requires a reference to the current cursor, just in case... * Added TODOs for caching the BView token, it's currently resolved for every single BView call that talks to the server... not good! git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31133 a95241bf-73f2-0310-859d-f6bbb57e9c96
1785 lines
41 KiB
C++
1785 lines
41 KiB
C++
/*
|
|
* Copyright (c) 2001-2008, Haiku, Inc.
|
|
* Distributed under the terms of the MIT license.
|
|
*
|
|
* Authors:
|
|
* DarkWyrm <bpmagic@columbus.rr.com>
|
|
* Adi Oanca <adioanca@gmail.com>
|
|
* Axel Dörfler, axeld@pinc-software.de
|
|
* Stephan Aßmus <superstippi@gmx.de>
|
|
* Marcus Overhagen <marcus@overhagen.de>
|
|
*/
|
|
|
|
|
|
#include "View.h"
|
|
|
|
#include "BitmapManager.h"
|
|
#include "Desktop.h"
|
|
#include "DrawingEngine.h"
|
|
#include "Overlay.h"
|
|
#include "ServerApp.h"
|
|
#include "ServerBitmap.h"
|
|
#include "ServerCursor.h"
|
|
#include "ServerPicture.h"
|
|
#include "ServerWindow.h"
|
|
#include "Window.h"
|
|
|
|
#include "drawing_support.h"
|
|
|
|
#include <List.h>
|
|
#include <Message.h>
|
|
#include <PortLink.h>
|
|
#include <View.h> // for resize modes
|
|
#include <WindowPrivate.h>
|
|
|
|
#include <GradientLinear.h>
|
|
#include <GradientRadial.h>
|
|
#include <GradientRadialFocus.h>
|
|
#include <GradientDiamond.h>
|
|
#include <GradientConic.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <new>
|
|
|
|
using std::nothrow;
|
|
|
|
|
|
void
|
|
resize_frame(IntRect& frame, uint32 resizingMode, int32 x, int32 y)
|
|
{
|
|
// follow with left side
|
|
if ((resizingMode & 0x0F00U) == _VIEW_RIGHT_ << 8)
|
|
frame.left += x;
|
|
else if ((resizingMode & 0x0F00U) == _VIEW_CENTER_ << 8)
|
|
frame.left += x / 2;
|
|
|
|
// follow with right side
|
|
if ((resizingMode & 0x000FU) == _VIEW_RIGHT_)
|
|
frame.right += x;
|
|
else if ((resizingMode & 0x000FU) == _VIEW_CENTER_)
|
|
frame.right += x / 2;
|
|
|
|
// follow with top side
|
|
if ((resizingMode & 0xF000U) == _VIEW_BOTTOM_ << 12)
|
|
frame.top += y;
|
|
else if ((resizingMode & 0xF000U) == _VIEW_CENTER_ << 12)
|
|
frame.top += y / 2;
|
|
|
|
// follow with bottom side
|
|
if ((resizingMode & 0x00F0U) == _VIEW_BOTTOM_ << 4)
|
|
frame.bottom += y;
|
|
else if ((resizingMode & 0x00F0U) == _VIEW_CENTER_ << 4)
|
|
frame.bottom += y / 2;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
View::View(IntRect frame, IntPoint scrollingOffset, const char* name,
|
|
int32 token, uint32 resizeMode, uint32 flags)
|
|
:
|
|
fName(name),
|
|
fToken(token),
|
|
|
|
fFrame(frame),
|
|
fScrollingOffset(scrollingOffset),
|
|
|
|
fViewColor((rgb_color){ 255, 255, 255, 255 }),
|
|
fDrawState(new (nothrow) DrawState),
|
|
fViewBitmap(NULL),
|
|
|
|
fResizeMode(resizeMode),
|
|
fFlags(flags),
|
|
|
|
// Views start visible by default
|
|
fHidden(false),
|
|
fVisible(true),
|
|
fBackgroundDirty(true),
|
|
fIsDesktopBackground(false),
|
|
|
|
fEventMask(0),
|
|
fEventOptions(0),
|
|
|
|
fWindow(NULL),
|
|
fParent(NULL),
|
|
|
|
fFirstChild(NULL),
|
|
fPreviousSibling(NULL),
|
|
fNextSibling(NULL),
|
|
fLastChild(NULL),
|
|
|
|
fCursor(NULL),
|
|
fPicture(NULL),
|
|
|
|
fLocalClipping((BRect)Bounds()),
|
|
fScreenClipping(),
|
|
fScreenClippingValid(false),
|
|
fUserClipping(NULL),
|
|
fScreenAndUserClipping(NULL)
|
|
{
|
|
if (fDrawState)
|
|
fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
|
|
}
|
|
|
|
|
|
View::~View()
|
|
{
|
|
if (fViewBitmap != NULL)
|
|
gBitmapManager->DeleteBitmap(fViewBitmap);
|
|
|
|
delete fScreenAndUserClipping;
|
|
delete fUserClipping;
|
|
delete fDrawState;
|
|
|
|
// if (fWindow && this == fWindow->TopView())
|
|
// fWindow->SetTopView(NULL);
|
|
|
|
if (fCursor)
|
|
fCursor->Release();
|
|
|
|
// iterate over children and delete each one
|
|
View* view = fFirstChild;
|
|
while (view) {
|
|
View* toast = view;
|
|
view = view->fNextSibling;
|
|
delete toast;
|
|
}
|
|
}
|
|
|
|
|
|
IntRect
|
|
View::Bounds() const
|
|
{
|
|
IntRect bounds(fScrollingOffset.x, fScrollingOffset.y,
|
|
fScrollingOffset.x + fFrame.Width(),
|
|
fScrollingOffset.y + fFrame.Height());
|
|
return bounds;
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertToVisibleInTopView(IntRect* bounds) const
|
|
{
|
|
*bounds = *bounds & Bounds();
|
|
// NOTE: this step is necessary even if we don't have a parent!
|
|
ConvertToParent(bounds);
|
|
|
|
if (fParent)
|
|
fParent->ConvertToVisibleInTopView(bounds);
|
|
}
|
|
|
|
|
|
void
|
|
View::AttachedToWindow(::Window* window)
|
|
{
|
|
fWindow = window;
|
|
|
|
// an ugly hack to detect the desktop background
|
|
if (window->Feel() == kDesktopWindowFeel && Parent() == TopView())
|
|
fIsDesktopBackground = true;
|
|
|
|
// insert view into local token space
|
|
if (fWindow != NULL) {
|
|
fWindow->ServerWindow()->App()->ViewTokens().SetToken(fToken,
|
|
B_HANDLER_TOKEN, this);
|
|
}
|
|
|
|
// attach child views as well
|
|
for (View* child = FirstChild(); child; child = child->NextSibling())
|
|
child->AttachedToWindow(window);
|
|
}
|
|
|
|
|
|
void
|
|
View::DetachedFromWindow()
|
|
{
|
|
// remove view from local token space
|
|
if (fWindow != NULL)
|
|
fWindow->ServerWindow()->App()->ViewTokens().RemoveToken(fToken);
|
|
|
|
fWindow = NULL;
|
|
// detach child views as well
|
|
for (View* child = FirstChild(); child; child = child->NextSibling())
|
|
child->DetachedFromWindow();
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
View::AddChild(View* view)
|
|
{
|
|
if (view->fParent) {
|
|
printf("View::AddChild() - View already has a parent\n");
|
|
return;
|
|
}
|
|
|
|
view->fParent = this;
|
|
|
|
if (!fLastChild) {
|
|
// no children yet
|
|
fFirstChild = view;
|
|
} else {
|
|
// append view to formerly last child
|
|
fLastChild->fNextSibling = view;
|
|
view->fPreviousSibling = fLastChild;
|
|
}
|
|
fLastChild = view;
|
|
|
|
view->UpdateVisibleDeep(fVisible);
|
|
|
|
if (view->IsVisible())
|
|
RebuildClipping(false);
|
|
|
|
if (fWindow) {
|
|
view->AttachedToWindow(fWindow);
|
|
|
|
if (view->IsVisible()) {
|
|
// trigger redraw
|
|
IntRect clippedFrame = view->Frame();
|
|
ConvertToVisibleInTopView(&clippedFrame);
|
|
BRegion* dirty = fWindow->GetRegion();
|
|
if (dirty) {
|
|
dirty->Set((clipping_rect)clippedFrame);
|
|
fWindow->MarkContentDirtyAsync(*dirty);
|
|
fWindow->RecycleRegion(dirty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
View::RemoveChild(View* view)
|
|
{
|
|
if (view == NULL || view->fParent != this) {
|
|
printf("View::RemoveChild(%p - %s) - View is not child of "
|
|
"this (%p) view!\n", view, view ? view->Name() : NULL, this);
|
|
return false;
|
|
}
|
|
|
|
view->fParent = NULL;
|
|
|
|
if (fLastChild == view)
|
|
fLastChild = view->fPreviousSibling;
|
|
// view->fNextSibling would be NULL
|
|
|
|
if (fFirstChild == view )
|
|
fFirstChild = view->fNextSibling;
|
|
// view->fPreviousSibling would be NULL
|
|
|
|
// connect child before and after view
|
|
if (view->fPreviousSibling)
|
|
view->fPreviousSibling->fNextSibling = view->fNextSibling;
|
|
|
|
if (view->fNextSibling)
|
|
view->fNextSibling->fPreviousSibling = view->fPreviousSibling;
|
|
|
|
// view has no siblings anymore
|
|
view->fPreviousSibling = NULL;
|
|
view->fNextSibling = NULL;
|
|
|
|
if (view->IsVisible()) {
|
|
Overlay* overlay = view->_Overlay();
|
|
if (overlay != NULL)
|
|
overlay->Hide();
|
|
|
|
RebuildClipping(false);
|
|
}
|
|
|
|
if (fWindow) {
|
|
view->DetachedFromWindow();
|
|
|
|
if (fVisible && view->IsVisible()) {
|
|
// trigger redraw
|
|
IntRect clippedFrame = view->Frame();
|
|
ConvertToVisibleInTopView(&clippedFrame);
|
|
BRegion* dirty = fWindow->GetRegion();
|
|
if (dirty) {
|
|
dirty->Set((clipping_rect)clippedFrame);
|
|
fWindow->MarkContentDirtyAsync(*dirty);
|
|
fWindow->RecycleRegion(dirty);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
View*
|
|
View::TopView()
|
|
{
|
|
// returns the top level view of the hirarchy,
|
|
// it doesn't have to be the top level of a window
|
|
|
|
if (fParent)
|
|
return fParent->TopView();
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
uint32
|
|
View::CountChildren(bool deep) const
|
|
{
|
|
uint32 count = 0;
|
|
for (View* child = FirstChild(); child; child = child->NextSibling()) {
|
|
count++;
|
|
if (deep) {
|
|
count += child->CountChildren(deep);
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
void
|
|
View::CollectTokensForChildren(BList* tokenMap) const
|
|
{
|
|
for (View* child = FirstChild(); child; child = child->NextSibling()) {
|
|
tokenMap->AddItem((void*)child);
|
|
child->CollectTokensForChildren(tokenMap);
|
|
}
|
|
}
|
|
|
|
|
|
#if 0
|
|
bool
|
|
View::MarkAt(DrawingEngine* engine, const BPoint& where, int32 level)
|
|
{
|
|
BRect rect(fFrame.left, fFrame.top, fFrame.right, fFrame.bottom);
|
|
|
|
if (Parent() != NULL) {
|
|
Parent()->ConvertToScreen(&rect);
|
|
if (!rect.Contains(where))
|
|
return false;
|
|
|
|
engine->StrokeRect(rect, (rgb_color){level * 30, level * 30, level * 30});
|
|
}
|
|
|
|
|
|
bool found = false;
|
|
for (View* child = FirstChild(); child; child = child->NextSibling()) {
|
|
found |= child->MarkAt(engine, where, level + 1);
|
|
}
|
|
|
|
if (!found) {
|
|
rgb_color color = {0};
|
|
switch (level % 2) {
|
|
case 0: color.green = rand() % 256; break;
|
|
case 1: color.blue = rand() % 256; break;
|
|
}
|
|
|
|
rect.InsetBy(1, 1);
|
|
//engine->FillRegion(fLocalClipping, (rgb_color){255, 255, 0, 10});
|
|
engine->StrokeRect(rect, color);
|
|
rect.InsetBy(1, 1);
|
|
engine->StrokeRect(rect, color);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
|
|
void
|
|
View::FindViews(uint32 flags, BObjectList<View>& list, int32& left)
|
|
{
|
|
if ((Flags() & flags) == flags) {
|
|
list.AddItem(this);
|
|
left--;
|
|
return;
|
|
}
|
|
|
|
for (View* child = FirstChild(); child; child = child->NextSibling()) {
|
|
child->FindViews(flags, list, left);
|
|
if (left == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
View*
|
|
View::ViewAt(const BPoint& where)
|
|
{
|
|
if (!fVisible)
|
|
return NULL;
|
|
|
|
IntRect frame = Frame();
|
|
if (Parent() != NULL)
|
|
Parent()->ConvertToScreen(&frame);
|
|
|
|
if (!frame.Contains(where))
|
|
return NULL;
|
|
|
|
for (View* child = FirstChild(); child; child = child->NextSibling()) {
|
|
View* view = child->ViewAt(where);
|
|
if (view != NULL)
|
|
return view;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
View::SetName(const char* string)
|
|
{
|
|
fName.SetTo(string);
|
|
}
|
|
|
|
|
|
void
|
|
View::SetFlags(uint32 flags)
|
|
{
|
|
fFlags = flags;
|
|
fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
|
|
}
|
|
|
|
|
|
void
|
|
View::SetDrawingOrigin(BPoint origin)
|
|
{
|
|
fDrawState->SetOrigin(origin);
|
|
|
|
// rebuild clipping
|
|
if (fDrawState->HasClipping())
|
|
RebuildClipping(false);
|
|
}
|
|
|
|
|
|
BPoint
|
|
View::DrawingOrigin() const
|
|
{
|
|
BPoint origin(fDrawState->Origin());
|
|
float scale = Scale();
|
|
|
|
origin.x *= scale;
|
|
origin.y *= scale;
|
|
|
|
return origin;
|
|
}
|
|
|
|
|
|
void
|
|
View::SetScale(float scale)
|
|
{
|
|
fDrawState->SetScale(scale);
|
|
|
|
// rebuild clipping
|
|
if (fDrawState->HasClipping())
|
|
RebuildClipping(false);
|
|
}
|
|
|
|
|
|
float
|
|
View::Scale() const
|
|
{
|
|
return CurrentState()->Scale();
|
|
}
|
|
|
|
|
|
void
|
|
View::SetUserClipping(const BRegion* region)
|
|
{
|
|
fDrawState->SetClippingRegion(region);
|
|
|
|
// rebuild clipping (for just this view)
|
|
RebuildClipping(false);
|
|
}
|
|
|
|
|
|
void
|
|
View::SetViewBitmap(ServerBitmap* bitmap, IntRect sourceRect,
|
|
IntRect destRect, int32 resizingMode, int32 options)
|
|
{
|
|
if (fViewBitmap != NULL) {
|
|
Overlay* overlay = _Overlay();
|
|
|
|
if (bitmap != NULL) {
|
|
// take over overlay token from current overlay (if it has any)
|
|
Overlay* newOverlay = bitmap->Overlay();
|
|
|
|
if (overlay != NULL && newOverlay != NULL)
|
|
newOverlay->TakeOverToken(overlay);
|
|
} else if (overlay != NULL)
|
|
overlay->Hide();
|
|
|
|
gBitmapManager->DeleteBitmap(fViewBitmap);
|
|
}
|
|
|
|
// the caller is allowed to delete the bitmap after setting the background
|
|
if (bitmap != NULL)
|
|
bitmap->Acquire();
|
|
|
|
fViewBitmap = bitmap;
|
|
fBitmapSource = sourceRect;
|
|
fBitmapDestination = destRect;
|
|
fBitmapResizingMode = resizingMode;
|
|
fBitmapOptions = options;
|
|
|
|
_UpdateOverlayView();
|
|
}
|
|
|
|
|
|
::Overlay*
|
|
View::_Overlay() const
|
|
{
|
|
if (fViewBitmap == NULL)
|
|
return NULL;
|
|
|
|
return fViewBitmap->Overlay();
|
|
}
|
|
|
|
|
|
void
|
|
View::_UpdateOverlayView() const
|
|
{
|
|
Overlay* overlay = _Overlay();
|
|
if (overlay == NULL)
|
|
return;
|
|
|
|
IntRect destination = fBitmapDestination;
|
|
ConvertToScreen(&destination);
|
|
|
|
overlay->Configure(fBitmapSource, destination);
|
|
}
|
|
|
|
|
|
/*!
|
|
This method is called whenever the window is resized or moved - would
|
|
be nice to have a better solution for this, though.
|
|
*/
|
|
void
|
|
View::UpdateOverlay()
|
|
{
|
|
if (!IsVisible())
|
|
return;
|
|
|
|
if (_Overlay() != NULL) {
|
|
_UpdateOverlayView();
|
|
} else {
|
|
// recursively ask children of this view
|
|
|
|
for (View* child = FirstChild(); child; child = child->NextSibling()) {
|
|
child->UpdateOverlay();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
View::ConvertToParent(BPoint* point) const
|
|
{
|
|
// remove scrolling offset and convert to parent coordinate space
|
|
point->x += fFrame.left - fScrollingOffset.x;
|
|
point->y += fFrame.top - fScrollingOffset.y;
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertToParent(IntPoint* point) const
|
|
{
|
|
// remove scrolling offset and convert to parent coordinate space
|
|
point->x += fFrame.left - fScrollingOffset.x;
|
|
point->y += fFrame.top - fScrollingOffset.y;
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertToParent(BRect* rect) const
|
|
{
|
|
// remove scrolling offset and convert to parent coordinate space
|
|
rect->OffsetBy(fFrame.left - fScrollingOffset.x,
|
|
fFrame.top - fScrollingOffset.y);
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertToParent(IntRect* rect) const
|
|
{
|
|
// remove scrolling offset and convert to parent coordinate space
|
|
rect->OffsetBy(fFrame.left - fScrollingOffset.x,
|
|
fFrame.top - fScrollingOffset.y);
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertToParent(BRegion* region) const
|
|
{
|
|
// remove scrolling offset and convert to parent coordinate space
|
|
region->OffsetBy(fFrame.left - fScrollingOffset.x,
|
|
fFrame.top - fScrollingOffset.y);
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertFromParent(BPoint* point) const
|
|
{
|
|
// convert from parent coordinate space amd add scrolling offset
|
|
point->x += fScrollingOffset.x - fFrame.left;
|
|
point->y += fScrollingOffset.y - fFrame.top;
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertFromParent(IntPoint* point) const
|
|
{
|
|
// convert from parent coordinate space amd add scrolling offset
|
|
point->x += fScrollingOffset.x - fFrame.left;
|
|
point->y += fScrollingOffset.y - fFrame.top;
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertFromParent(BRect* rect) const
|
|
{
|
|
// convert from parent coordinate space amd add scrolling offset
|
|
rect->OffsetBy(fScrollingOffset.x - fFrame.left,
|
|
fScrollingOffset.y - fFrame.top);
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertFromParent(IntRect* rect) const
|
|
{
|
|
// convert from parent coordinate space amd add scrolling offset
|
|
rect->OffsetBy(fScrollingOffset.x - fFrame.left,
|
|
fScrollingOffset.y - fFrame.top);
|
|
}
|
|
|
|
|
|
void
|
|
View::ConvertFromParent(BRegion* region) const
|
|
{
|
|
// convert from parent coordinate space amd add scrolling offset
|
|
region->OffsetBy(fScrollingOffset.x - fFrame.left,
|
|
fScrollingOffset.y - fFrame.top);
|
|
}
|
|
|
|
//! converts a point from local to screen coordinate system
|
|
void
|
|
View::ConvertToScreen(BPoint* pt) const
|
|
{
|
|
ConvertToParent(pt);
|
|
|
|
if (fParent)
|
|
fParent->ConvertToScreen(pt);
|
|
}
|
|
|
|
|
|
//! converts a point from local to screen coordinate system
|
|
void
|
|
View::ConvertToScreen(IntPoint* pt) const
|
|
{
|
|
ConvertToParent(pt);
|
|
|
|
if (fParent)
|
|
fParent->ConvertToScreen(pt);
|
|
}
|
|
|
|
|
|
//! converts a rect from local to screen coordinate system
|
|
void
|
|
View::ConvertToScreen(BRect* rect) const
|
|
{
|
|
BPoint offset(0.0, 0.0);
|
|
ConvertToScreen(&offset);
|
|
|
|
rect->OffsetBy(offset);
|
|
}
|
|
|
|
|
|
//! converts a rect from local to screen coordinate system
|
|
void
|
|
View::ConvertToScreen(IntRect* rect) const
|
|
{
|
|
BPoint offset(0.0, 0.0);
|
|
ConvertToScreen(&offset);
|
|
|
|
rect->OffsetBy(offset);
|
|
}
|
|
|
|
|
|
//! converts a region from local to screen coordinate system
|
|
void
|
|
View::ConvertToScreen(BRegion* region) const
|
|
{
|
|
BPoint offset(0.0, 0.0);
|
|
ConvertToScreen(&offset);
|
|
|
|
region->OffsetBy((int)offset.x, (int)offset.y);
|
|
}
|
|
|
|
|
|
//! converts a point from screen to local coordinate system
|
|
void
|
|
View::ConvertFromScreen(BPoint* pt) const
|
|
{
|
|
ConvertFromParent(pt);
|
|
|
|
if (fParent)
|
|
fParent->ConvertFromScreen(pt);
|
|
}
|
|
|
|
|
|
//! converts a point from screen to local coordinate system
|
|
void
|
|
View::ConvertFromScreen(IntPoint* pt) const
|
|
{
|
|
ConvertFromParent(pt);
|
|
|
|
if (fParent)
|
|
fParent->ConvertFromScreen(pt);
|
|
}
|
|
|
|
|
|
//! converts a rect from screen to local coordinate system
|
|
void
|
|
View::ConvertFromScreen(BRect* rect) const
|
|
{
|
|
BPoint offset(0.0, 0.0);
|
|
ConvertFromScreen(&offset);
|
|
|
|
rect->OffsetBy(offset.x, offset.y);
|
|
}
|
|
|
|
|
|
//! converts a rect from screen to local coordinate system
|
|
void
|
|
View::ConvertFromScreen(IntRect* rect) const
|
|
{
|
|
BPoint offset(0.0, 0.0);
|
|
ConvertFromScreen(&offset);
|
|
|
|
rect->OffsetBy((int)offset.x, (int)offset.y);
|
|
}
|
|
|
|
|
|
//! converts a region from screen to local coordinate system
|
|
void
|
|
View::ConvertFromScreen(BRegion* region) const
|
|
{
|
|
BPoint offset(0.0, 0.0);
|
|
ConvertFromScreen(&offset);
|
|
|
|
region->OffsetBy((int)offset.x, (int)offset.y);
|
|
}
|
|
|
|
|
|
//! converts a point from local *drawing* to screen coordinate system
|
|
void
|
|
View::ConvertToScreenForDrawing(BPoint* point) const
|
|
{
|
|
fDrawState->Transform(point);
|
|
// NOTE: from here on, don't use the
|
|
// "*ForDrawing()" versions of the parent!
|
|
ConvertToScreen(point);
|
|
}
|
|
|
|
|
|
//! converts a rect from local *drawing* to screen coordinate system
|
|
void
|
|
View::ConvertToScreenForDrawing(BRect* rect) const
|
|
{
|
|
fDrawState->Transform(rect);
|
|
// NOTE: from here on, don't use the
|
|
// "*ForDrawing()" versions of the parent!
|
|
ConvertToScreen(rect);
|
|
}
|
|
|
|
|
|
//! converts a region from local *drawing* to screen coordinate system
|
|
void
|
|
View::ConvertToScreenForDrawing(BRegion* region) const
|
|
{
|
|
fDrawState->Transform(region);
|
|
// NOTE: from here on, don't use the
|
|
// "*ForDrawing()" versions of the parent!
|
|
ConvertToScreen(region);
|
|
}
|
|
|
|
|
|
//! converts a gradient from local *drawing* to screen coordinate system
|
|
void
|
|
View::ConvertToScreenForDrawing(BGradient* gradient) const
|
|
{
|
|
switch(gradient->GetType()) {
|
|
case BGradient::TYPE_LINEAR: {
|
|
BGradientLinear* linear = (BGradientLinear*) gradient;
|
|
BPoint start = linear->Start();
|
|
BPoint end = linear->End();
|
|
fDrawState->Transform(&start);
|
|
ConvertToScreen(&start);
|
|
fDrawState->Transform(&end);
|
|
ConvertToScreen(&end);
|
|
linear->SetStart(start);
|
|
linear->SetEnd(end);
|
|
linear->SortColorStopsByOffset();
|
|
break;
|
|
}
|
|
case BGradient::TYPE_RADIAL: {
|
|
BGradientRadial* radial = (BGradientRadial*) gradient;
|
|
BPoint center = radial->Center();
|
|
fDrawState->Transform(¢er);
|
|
ConvertToScreen(¢er);
|
|
radial->SetCenter(center);
|
|
radial->SortColorStopsByOffset();
|
|
break;
|
|
}
|
|
case BGradient::TYPE_RADIAL_FOCUS: {
|
|
BGradientRadialFocus* radialFocus = (BGradientRadialFocus*) gradient;
|
|
BPoint center = radialFocus->Center();
|
|
BPoint focal = radialFocus->Focal();
|
|
fDrawState->Transform(¢er);
|
|
ConvertToScreen(¢er);
|
|
fDrawState->Transform(&focal);
|
|
ConvertToScreen(&focal);
|
|
radialFocus->SetCenter(center);
|
|
radialFocus->SetFocal(focal);
|
|
radialFocus->SortColorStopsByOffset();
|
|
break;
|
|
}
|
|
case BGradient::TYPE_DIAMOND: {
|
|
BGradientDiamond* diamond = (BGradientDiamond*) gradient;
|
|
BPoint center = diamond->Center();
|
|
fDrawState->Transform(¢er);
|
|
ConvertToScreen(¢er);
|
|
diamond->SetCenter(center);
|
|
diamond->SortColorStopsByOffset();
|
|
break;
|
|
}
|
|
case BGradient::TYPE_CONIC: {
|
|
BGradientConic* conic = (BGradientConic*) gradient;
|
|
BPoint center = conic->Center();
|
|
fDrawState->Transform(¢er);
|
|
ConvertToScreen(¢er);
|
|
conic->SetCenter(center);
|
|
conic->SortColorStopsByOffset();
|
|
break;
|
|
}
|
|
case BGradient::TYPE_NONE: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! converts points from local *drawing* to screen coordinate system
|
|
void
|
|
View::ConvertToScreenForDrawing(BPoint* dst, const BPoint* src, int32 num) const
|
|
{
|
|
// TODO: optimize this, it should be smarter
|
|
while (num--) {
|
|
*dst = *src;
|
|
fDrawState->Transform(dst);
|
|
// NOTE: from here on, don't use the
|
|
// "*ForDrawing()" versions of the parent!
|
|
ConvertToScreen(dst);
|
|
src++;
|
|
dst++;
|
|
}
|
|
}
|
|
|
|
|
|
//! converts rects from local *drawing* to screen coordinate system
|
|
void
|
|
View::ConvertToScreenForDrawing(BRect* dst, const BRect* src, int32 num) const
|
|
{
|
|
// TODO: optimize this, it should be smarter
|
|
while (num--) {
|
|
*dst = *src;
|
|
fDrawState->Transform(dst);
|
|
// NOTE: from here on, don't use the
|
|
// "*ForDrawing()" versions of the parent!
|
|
ConvertToScreen(dst);
|
|
src++;
|
|
dst++;
|
|
}
|
|
}
|
|
|
|
|
|
//! converts regions from local *drawing* to screen coordinate system
|
|
void
|
|
View::ConvertToScreenForDrawing(BRegion* dst, const BRegion* src, int32 num) const
|
|
{
|
|
// TODO: optimize this, it should be smarter
|
|
while (num--) {
|
|
*dst = *src;
|
|
fDrawState->Transform(dst);
|
|
// NOTE: from here on, don't use the
|
|
// "*ForDrawing()" versions of the parent!
|
|
ConvertToScreen(dst);
|
|
src++;
|
|
dst++;
|
|
}
|
|
}
|
|
|
|
|
|
//! converts a point from screen to local coordinate system
|
|
void
|
|
View::ConvertFromScreenForDrawing(BPoint* point) const
|
|
{
|
|
ConvertFromScreen(point);
|
|
fDrawState->InverseTransform(point);
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
View::MoveBy(int32 x, int32 y, BRegion* dirtyRegion)
|
|
{
|
|
if (x == 0 && y == 0)
|
|
return;
|
|
|
|
fFrame.OffsetBy(x, y);
|
|
|
|
// to move on screen, we must not be hidden and we must have a parent
|
|
if (fVisible && fParent && dirtyRegion) {
|
|
#if 1
|
|
// based on redraw on new location
|
|
// the place were we are now visible
|
|
IntRect newVisibleBounds(Bounds());
|
|
// we can use the frame of the old
|
|
// local clipping to see which parts need invalidation
|
|
IntRect oldVisibleBounds(newVisibleBounds);
|
|
oldVisibleBounds.OffsetBy(-x, -y);
|
|
ConvertToScreen(&oldVisibleBounds);
|
|
|
|
ConvertToVisibleInTopView(&newVisibleBounds);
|
|
|
|
dirtyRegion->Include((clipping_rect)oldVisibleBounds);
|
|
// newVisibleBounds already is in screen coords
|
|
dirtyRegion->Include((clipping_rect)newVisibleBounds);
|
|
#else
|
|
// blitting version, invalidates
|
|
// old contents
|
|
IntRect oldVisibleBounds(Bounds());
|
|
IntRect newVisibleBounds(oldVisibleBounds);
|
|
oldVisibleBounds.OffsetBy(-x, -y);
|
|
ConvertToScreen(&oldVisibleBounds);
|
|
|
|
// NOTE: using ConvertToVisibleInTopView()
|
|
// instead of ConvertToScreen()! see below
|
|
ConvertToVisibleInTopView(&newVisibleBounds);
|
|
|
|
newVisibleBounds.OffsetBy(-x, -y);
|
|
|
|
// clipping oldVisibleBounds to newVisibleBounds
|
|
// makes sure we don't copy parts hidden under
|
|
// parent views
|
|
BRegion* region = fWindow->GetRegion();
|
|
if (region) {
|
|
region->Set(oldVisibleBounds & newVisibleBounds);
|
|
fWindow->CopyContents(region, x, y);
|
|
|
|
region->Set(oldVisibleBounds);
|
|
newVisibleBounds.OffsetBy(x, y);
|
|
region->Exclude((clipping_rect)newVisibleBounds);
|
|
dirtyRegion->Include(dirty);
|
|
|
|
fWindow->RecycleRegion(region);
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
if (!fParent) {
|
|
// the top view's screen clipping does not change,
|
|
// because no parts are clipped away from parent
|
|
// views
|
|
_MoveScreenClipping(x, y, true);
|
|
} else {
|
|
// parts might have been revealed from underneath
|
|
// the parent, or might now be hidden underneath
|
|
// the parent, this is taken care of when building
|
|
// the screen clipping
|
|
InvalidateScreenClipping();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
View::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
|
|
{
|
|
if (x == 0 && y == 0)
|
|
return;
|
|
|
|
fFrame.right += x;
|
|
fFrame.bottom += y;
|
|
|
|
if (fVisible && dirtyRegion) {
|
|
IntRect oldBounds(Bounds());
|
|
oldBounds.right -= x;
|
|
oldBounds.bottom -= y;
|
|
|
|
BRegion* dirty = fWindow->GetRegion();
|
|
if (!dirty)
|
|
return;
|
|
|
|
dirty->Set((clipping_rect)Bounds());
|
|
dirty->Include((clipping_rect)oldBounds);
|
|
|
|
if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) {
|
|
// the dirty region is just the difference of
|
|
// old and new bounds
|
|
dirty->Exclude((clipping_rect)(oldBounds & Bounds()));
|
|
}
|
|
|
|
InvalidateScreenClipping();
|
|
|
|
if (dirty->CountRects() > 0) {
|
|
if ((fFlags & B_DRAW_ON_CHILDREN) == 0) {
|
|
// exclude children, they are expected to
|
|
// include their own dirty regions in ParentResized()
|
|
for (View* child = FirstChild(); child;
|
|
child = child->NextSibling()) {
|
|
if (!child->IsVisible())
|
|
continue;
|
|
IntRect previousChildVisible(
|
|
child->Frame() & oldBounds & Bounds());
|
|
if (dirty->Frame().Intersects(previousChildVisible)) {
|
|
dirty->Exclude((clipping_rect)previousChildVisible);
|
|
}
|
|
}
|
|
}
|
|
|
|
ConvertToScreen(dirty);
|
|
dirtyRegion->Include(dirty);
|
|
}
|
|
fWindow->RecycleRegion(dirty);
|
|
}
|
|
|
|
// layout the children
|
|
for (View* child = FirstChild(); child; child = child->NextSibling())
|
|
child->ParentResized(x, y, dirtyRegion);
|
|
|
|
// view bitmap
|
|
|
|
resize_frame(fBitmapDestination, fBitmapResizingMode, x, y);
|
|
|
|
// at this point, children are at their new locations,
|
|
// so we can rebuild the clipping
|
|
// TODO: when the implementation of Hide() and Show() is
|
|
// complete, see if this should be avoided
|
|
RebuildClipping(false);
|
|
}
|
|
|
|
|
|
void
|
|
View::ParentResized(int32 x, int32 y, BRegion* dirtyRegion)
|
|
{
|
|
IntRect newFrame = fFrame;
|
|
resize_frame(newFrame, fResizeMode & 0x0000ffff, x, y);
|
|
|
|
if (newFrame != fFrame) {
|
|
// careful, MoveBy will change fFrame
|
|
int32 widthDiff = (int32)(newFrame.Width() - fFrame.Width());
|
|
int32 heightDiff = (int32)(newFrame.Height() - fFrame.Height());
|
|
|
|
MoveBy(newFrame.left - fFrame.left,
|
|
newFrame.top - fFrame.top, dirtyRegion);
|
|
|
|
ResizeBy(widthDiff, heightDiff, dirtyRegion);
|
|
} else {
|
|
// TODO: this covers the fact that our screen clipping might change
|
|
// when the parent changes its size, even though our frame stays
|
|
// the same - there might be a way to test for this, but axeld doesn't
|
|
// know, stippi should look into this when he's back :)
|
|
InvalidateScreenClipping();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
View::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion)
|
|
{
|
|
if (!fVisible || !fWindow) {
|
|
fScrollingOffset.x += x;
|
|
fScrollingOffset.y += y;
|
|
return;
|
|
}
|
|
|
|
// blitting version, invalidates
|
|
// old contents
|
|
|
|
// remember old bounds for tracking dirty region
|
|
IntRect oldBounds(Bounds());
|
|
|
|
// NOTE: using ConvertToVisibleInTopView()
|
|
// instead of ConvertToScreen(), this makes
|
|
// sure we don't try to move or invalidate an
|
|
// area hidden underneath the parent view
|
|
ConvertToVisibleInTopView(&oldBounds);
|
|
|
|
// find the area of the view that can be scrolled,
|
|
// contents are shifted in the opposite direction from scrolling
|
|
IntRect stillVisibleBounds(oldBounds);
|
|
stillVisibleBounds.OffsetBy(x, y);
|
|
stillVisibleBounds = stillVisibleBounds & oldBounds;
|
|
|
|
fScrollingOffset.x += x;
|
|
fScrollingOffset.y += y;
|
|
|
|
// do the blit, this will make sure
|
|
// that other more complex dirty regions
|
|
// are taken care of
|
|
BRegion* copyRegion = fWindow->GetRegion();
|
|
if (!copyRegion)
|
|
return;
|
|
copyRegion->Set((clipping_rect)stillVisibleBounds);
|
|
fWindow->CopyContents(copyRegion, -x, -y);
|
|
|
|
// find the dirty region as far as we are
|
|
// concerned
|
|
BRegion* dirty = copyRegion;
|
|
// reuse copyRegion and call it dirty
|
|
|
|
dirty->Set((clipping_rect)oldBounds);
|
|
stillVisibleBounds.OffsetBy(-x, -y);
|
|
dirty->Exclude((clipping_rect)stillVisibleBounds);
|
|
dirtyRegion->Include(dirty);
|
|
|
|
fWindow->RecycleRegion(dirty);
|
|
|
|
// the screen clipping of this view and it's
|
|
// childs is no longer valid
|
|
InvalidateScreenClipping();
|
|
RebuildClipping(false);
|
|
}
|
|
|
|
|
|
void
|
|
View::CopyBits(IntRect src, IntRect dst, BRegion& windowContentClipping)
|
|
{
|
|
if (!fVisible || !fWindow)
|
|
return;
|
|
|
|
// TODO: confirm that in R5 this call is affected by origin and scale
|
|
|
|
// blitting version
|
|
|
|
int32 xOffset = dst.left - src.left;
|
|
int32 yOffset = dst.top - src.top;
|
|
|
|
// figure out which part can be blittet
|
|
IntRect visibleSrc(src);
|
|
ConvertToVisibleInTopView(&visibleSrc);
|
|
|
|
IntRect visibleSrcAtDest(src);
|
|
visibleSrcAtDest.OffsetBy(xOffset, yOffset);
|
|
ConvertToVisibleInTopView(&visibleSrcAtDest);
|
|
|
|
// clip src to visible at dest
|
|
visibleSrcAtDest.OffsetBy(-xOffset, -yOffset);
|
|
visibleSrc = visibleSrc & visibleSrcAtDest;
|
|
|
|
// do the blit, this will make sure
|
|
// that other more complex dirty regions
|
|
// are taken care of
|
|
BRegion* copyRegion = fWindow->GetRegion();
|
|
if (!copyRegion)
|
|
return;
|
|
|
|
// move src rect to destination here for efficiency reasons
|
|
visibleSrc.OffsetBy(xOffset, yOffset);
|
|
|
|
// we need to interstect the copyRegion two times, onces
|
|
// at the source and once at the destination (here done
|
|
// the other way arround but it doesn't matter)
|
|
// the reason for this is that we are not supposed to visually
|
|
// copy children in the source rect and neither to copy onto
|
|
// children in the destination rect...
|
|
copyRegion->Set((clipping_rect)visibleSrc);
|
|
BRegion *screenAndUserClipping
|
|
= &ScreenAndUserClipping(&windowContentClipping);
|
|
copyRegion->IntersectWith(screenAndUserClipping);
|
|
copyRegion->OffsetBy(-xOffset, -yOffset);
|
|
copyRegion->IntersectWith(screenAndUserClipping);
|
|
|
|
// do the actual blit
|
|
fWindow->CopyContents(copyRegion, xOffset, yOffset);
|
|
|
|
// find the dirty region as far as we are concerned
|
|
IntRect dirtyDst(dst);
|
|
ConvertToVisibleInTopView(&dirtyDst);
|
|
|
|
BRegion* dirty = fWindow->GetRegion();
|
|
if (!dirty) {
|
|
fWindow->RecycleRegion(copyRegion);
|
|
return;
|
|
}
|
|
|
|
// offset copyRegion to destination again
|
|
copyRegion->OffsetBy(xOffset, yOffset);
|
|
// start with destination given by user
|
|
dirty->Set((clipping_rect)dirtyDst);
|
|
// exclude the part that we could copy
|
|
dirty->Exclude(copyRegion);
|
|
|
|
dirty->IntersectWith(screenAndUserClipping);
|
|
fWindow->MarkContentDirty(*dirty);
|
|
|
|
fWindow->RecycleRegion(dirty);
|
|
fWindow->RecycleRegion(copyRegion);
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
View::PushState()
|
|
{
|
|
DrawState* newState = fDrawState->PushState();
|
|
if (newState) {
|
|
fDrawState = newState;
|
|
fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
View::PopState()
|
|
{
|
|
if (fDrawState->PreviousState() == NULL) {
|
|
fprintf(stderr, "WARNING: User called BView(%s)::PopState(), "
|
|
"but there is NO state on stack!\n", Name());
|
|
return;
|
|
}
|
|
|
|
bool rebuildClipping = fDrawState->HasAdditionalClipping();
|
|
|
|
fDrawState = fDrawState->PopState();
|
|
fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE);
|
|
|
|
// rebuild clipping
|
|
// (the clipping from the popped state is not effective anymore)
|
|
if (rebuildClipping)
|
|
RebuildClipping(false);
|
|
}
|
|
|
|
|
|
void
|
|
View::SetEventMask(uint32 eventMask, uint32 options)
|
|
{
|
|
fEventMask = eventMask;
|
|
fEventOptions = options;
|
|
}
|
|
|
|
|
|
void
|
|
View::SetCursor(ServerCursor *cursor)
|
|
{
|
|
if (cursor == fCursor)
|
|
return;
|
|
|
|
if (fCursor)
|
|
fCursor->Release();
|
|
|
|
fCursor = cursor;
|
|
|
|
if (fCursor)
|
|
fCursor->Acquire();
|
|
}
|
|
|
|
|
|
void
|
|
View::SetPicture(ServerPicture *picture)
|
|
{
|
|
fPicture = picture;
|
|
}
|
|
|
|
|
|
void
|
|
View::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
|
|
BRegion* windowContentClipping, bool deep)
|
|
{
|
|
if (!fVisible) {
|
|
// child views cannot be visible either
|
|
return;
|
|
}
|
|
|
|
if (fViewBitmap != NULL || fViewColor != B_TRANSPARENT_COLOR) {
|
|
// we can only draw within our own area
|
|
BRegion* redraw;
|
|
if ((fFlags & B_DRAW_ON_CHILDREN) != 0) {
|
|
// The client may actually want to prevent the background to
|
|
// be painted outside the user clipping.
|
|
redraw = fWindow->GetRegion(
|
|
ScreenAndUserClipping(windowContentClipping));
|
|
} else {
|
|
// Ignore user clipping as in BeOS for painting the background.
|
|
redraw = fWindow->GetRegion(
|
|
_ScreenClipping(windowContentClipping));
|
|
}
|
|
if (!redraw)
|
|
return;
|
|
// add the current clipping
|
|
redraw->IntersectWith(effectiveClipping);
|
|
|
|
Overlay* overlayCookie = _Overlay();
|
|
|
|
if (fViewBitmap != NULL && overlayCookie == NULL) {
|
|
// draw view bitmap
|
|
// TODO: support other options!
|
|
BRect rect = fBitmapDestination;
|
|
ConvertToScreenForDrawing(&rect);
|
|
|
|
align_rect_to_pixels(&rect);
|
|
|
|
if (fBitmapOptions & B_TILE_BITMAP_Y) {
|
|
// move rect up as much as needed
|
|
while (rect.top > redraw->Frame().top)
|
|
rect.OffsetBy(0.0, -(rect.Height() + 1));
|
|
}
|
|
if (fBitmapOptions & B_TILE_BITMAP_X) {
|
|
// move rect left as much as needed
|
|
while (rect.left > redraw->Frame().left)
|
|
rect.OffsetBy(-(rect.Width() + 1), 0.0);
|
|
}
|
|
|
|
// XXX: locking removed because the Window keeps the engine locked
|
|
// because it keeps track of syncing right now
|
|
|
|
// lock the drawing engine for as long as we need the clipping
|
|
// to be valid
|
|
if (rect.IsValid()/* && drawingEngine->Lock()*/) {
|
|
drawingEngine->ConstrainClippingRegion(redraw);
|
|
|
|
drawing_mode oldMode;
|
|
drawingEngine->SetDrawingMode(B_OP_COPY, oldMode);
|
|
|
|
if (fBitmapOptions & B_TILE_BITMAP) {
|
|
// tile across entire view
|
|
|
|
float start = rect.left;
|
|
while (rect.top < redraw->Frame().bottom) {
|
|
while (rect.left < redraw->Frame().right) {
|
|
drawingEngine->DrawBitmap(fViewBitmap,
|
|
fBitmapSource, rect, fBitmapOptions);
|
|
rect.OffsetBy(rect.Width() + 1, 0.0);
|
|
}
|
|
rect.OffsetBy(start - rect.left, rect.Height() + 1);
|
|
}
|
|
// nothing left to be drawn
|
|
redraw->MakeEmpty();
|
|
} else if (fBitmapOptions & B_TILE_BITMAP_X) {
|
|
// tile in x direction
|
|
|
|
while (rect.left < redraw->Frame().right) {
|
|
drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
|
|
rect, fBitmapOptions);
|
|
rect.OffsetBy(rect.Width() + 1, 0.0);
|
|
}
|
|
// remove horizontal stripe from clipping
|
|
rect.left = redraw->Frame().left;
|
|
rect.right = redraw->Frame().right;
|
|
redraw->Exclude(rect);
|
|
} else if (fBitmapOptions & B_TILE_BITMAP_Y) {
|
|
// tile in y direction
|
|
|
|
while (rect.top < redraw->Frame().bottom) {
|
|
drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
|
|
rect, fBitmapOptions);
|
|
rect.OffsetBy(0.0, rect.Height() + 1);
|
|
}
|
|
// remove vertical stripe from clipping
|
|
rect.top = redraw->Frame().top;
|
|
rect.bottom = redraw->Frame().bottom;
|
|
redraw->Exclude(rect);
|
|
} else {
|
|
// no tiling at all
|
|
|
|
drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource,
|
|
rect, fBitmapOptions);
|
|
redraw->Exclude(rect);
|
|
}
|
|
|
|
drawingEngine->SetDrawingMode(oldMode);
|
|
|
|
// NOTE: It is ok not to reset the clipping, that
|
|
// would only waste time
|
|
// drawingEngine->Unlock();
|
|
}
|
|
|
|
}
|
|
|
|
if (fViewColor != B_TRANSPARENT_COLOR) {
|
|
// fill visible region with view color,
|
|
// this version of FillRegion ignores any
|
|
// clipping, that's why "redraw" needs to
|
|
// be correct
|
|
// see #634
|
|
// if (redraw->Frame().left < 0 || redraw->Frame().top < 0) {
|
|
// char message[1024];
|
|
// BRect c = effectiveClipping->Frame();
|
|
// BRect w = windowContentClipping->Frame();
|
|
// BRect r = redraw->Frame();
|
|
// sprintf(message, "invalid background: current clipping: (%d, %d)->(%d, %d), "
|
|
// "window content: (%d, %d)->(%d, %d), redraw: (%d, %d)->(%d, %d)",
|
|
// (int)c.left, (int)c.top, (int)c.right, (int)c.bottom,
|
|
// (int)w.left, (int)w.top, (int)w.right, (int)w.bottom,
|
|
// (int)r.left, (int)r.top, (int)r.right, (int)r.bottom);
|
|
// debugger(message);
|
|
// }
|
|
|
|
drawingEngine->FillRegion(*redraw, overlayCookie != NULL
|
|
? overlayCookie->Color() : fViewColor);
|
|
}
|
|
|
|
fWindow->RecycleRegion(redraw);
|
|
}
|
|
|
|
fBackgroundDirty = false;
|
|
|
|
// let children draw
|
|
if (deep) {
|
|
for (View* child = FirstChild(); child; child = child->NextSibling()) {
|
|
child->Draw(drawingEngine, effectiveClipping,
|
|
windowContentClipping, deep);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
View::MouseDown(BMessage* message, BPoint where)
|
|
{
|
|
// empty hook method
|
|
}
|
|
|
|
|
|
void
|
|
View::MouseUp(BMessage* message, BPoint where)
|
|
{
|
|
// empty hook method
|
|
}
|
|
|
|
|
|
void
|
|
View::MouseMoved(BMessage* message, BPoint where)
|
|
{
|
|
// empty hook method
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
View::SetHidden(bool hidden)
|
|
{
|
|
if (fHidden != hidden) {
|
|
fHidden = hidden;
|
|
|
|
// recurse into children and update their visible flag
|
|
bool oldVisible = fVisible;
|
|
UpdateVisibleDeep(fParent ? fParent->IsVisible() : !fHidden);
|
|
if (oldVisible != fVisible) {
|
|
// Include or exclude us from the parent area, and update the
|
|
// children's clipping as well when the view will be visible
|
|
if (fParent)
|
|
fParent->RebuildClipping(fVisible);
|
|
else
|
|
RebuildClipping(fVisible);
|
|
|
|
if (fWindow) {
|
|
// trigger a redraw
|
|
IntRect clippedBounds = Bounds();
|
|
ConvertToVisibleInTopView(&clippedBounds);
|
|
BRegion* dirty = fWindow->GetRegion();
|
|
if (!dirty)
|
|
return;
|
|
dirty->Set((clipping_rect)clippedBounds);
|
|
fWindow->MarkContentDirty(*dirty);
|
|
fWindow->RecycleRegion(dirty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
View::IsHidden() const
|
|
{
|
|
return fHidden;
|
|
}
|
|
|
|
|
|
void
|
|
View::UpdateVisibleDeep(bool parentVisible)
|
|
{
|
|
bool wasVisible = fVisible;
|
|
|
|
fVisible = parentVisible && !fHidden;
|
|
for (View* child = FirstChild(); child; child = child->NextSibling())
|
|
child->UpdateVisibleDeep(fVisible);
|
|
|
|
// overlay handling
|
|
|
|
Overlay* overlay = _Overlay();
|
|
if (overlay == NULL)
|
|
return;
|
|
|
|
if (fVisible && !wasVisible)
|
|
_UpdateOverlayView();
|
|
else if (!fVisible && wasVisible)
|
|
overlay->Hide();
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
View::MarkBackgroundDirty()
|
|
{
|
|
if (fBackgroundDirty)
|
|
return;
|
|
fBackgroundDirty = true;
|
|
for (View* child = FirstChild(); child; child = child->NextSibling())
|
|
child->MarkBackgroundDirty();
|
|
}
|
|
|
|
|
|
void
|
|
View::AddTokensForViewsInRegion(BPrivate::PortLink& link, BRegion& region,
|
|
BRegion* windowContentClipping)
|
|
{
|
|
if (!fVisible)
|
|
return;
|
|
|
|
{
|
|
// NOTE: use scope in order to reduce stack space requirements
|
|
|
|
// This check will prevent descending the view hierarchy
|
|
// any further than necessary
|
|
IntRect screenBounds(Bounds());
|
|
ConvertToScreen(&screenBounds);
|
|
if (!region.Intersects((clipping_rect)screenBounds))
|
|
return;
|
|
|
|
// Unfortunately, we intersecting another region, but otherwise
|
|
// we couldn't provide the exact update rect to the client
|
|
BRegion localDirty = _ScreenClipping(windowContentClipping);
|
|
localDirty.IntersectWith(®ion);
|
|
if (localDirty.CountRects() > 0) {
|
|
link.Attach<int32>(fToken);
|
|
link.Attach<BRect>(localDirty.Frame());
|
|
}
|
|
}
|
|
|
|
for (View* child = FirstChild(); child; child = child->NextSibling())
|
|
child->AddTokensForViewsInRegion(link, region, windowContentClipping);
|
|
}
|
|
|
|
|
|
void
|
|
View::PrintToStream() const
|
|
{
|
|
printf("View: %s\n", Name());
|
|
printf(" fToken: %ld\n", fToken);
|
|
printf(" fFrame: IntRect(%ld, %ld, %ld, %ld)\n", fFrame.left, fFrame.top, fFrame.right, fFrame.bottom);
|
|
printf(" fScrollingOffset: IntPoint(%ld, %ld)\n", fScrollingOffset.x, fScrollingOffset.y);
|
|
printf(" fHidden: %d\n", fHidden);
|
|
printf(" fVisible: %d\n", fVisible);
|
|
printf(" fWindow: %p\n", fWindow);
|
|
printf(" fParent: %p\n", fParent);
|
|
printf(" fLocalClipping:\n");
|
|
fLocalClipping.PrintToStream();
|
|
printf(" fScreenClipping:\n");
|
|
fScreenClipping.PrintToStream();
|
|
printf(" valid: %d\n", fScreenClippingValid);
|
|
|
|
printf(" fUserClipping:\n");
|
|
if (fUserClipping != NULL)
|
|
fUserClipping->PrintToStream();
|
|
else
|
|
printf(" none\n");
|
|
|
|
printf(" fScreenAndUserClipping:\n");
|
|
if (fScreenAndUserClipping != NULL)
|
|
fScreenAndUserClipping->PrintToStream();
|
|
else
|
|
printf(" invalid\n");
|
|
|
|
printf(" state:\n");
|
|
printf(" user clipping: %d\n", fDrawState->HasClipping());
|
|
BPoint origin = fDrawState->CombinedOrigin();
|
|
printf(" origin: BPoint(%.1f, %.1f)\n", origin.x, origin.y);
|
|
printf(" scale: %.2f\n", fDrawState->CombinedScale());
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
void
|
|
View::RebuildClipping(bool deep)
|
|
{
|
|
// the clipping spans over the bounds area
|
|
fLocalClipping.Set((clipping_rect)Bounds());
|
|
|
|
if (View* child = FirstChild()) {
|
|
// if this view does not draw over children,
|
|
// exclude all children from the clipping
|
|
if ((fFlags & B_DRAW_ON_CHILDREN) == 0) {
|
|
BRegion* childrenRegion = fWindow->GetRegion();
|
|
if (!childrenRegion)
|
|
return;
|
|
|
|
for (; child; child = child->NextSibling()) {
|
|
if (child->IsVisible())
|
|
childrenRegion->Include((clipping_rect)child->Frame());
|
|
}
|
|
|
|
fLocalClipping.Exclude(childrenRegion);
|
|
fWindow->RecycleRegion(childrenRegion);
|
|
}
|
|
// if the operation is "deep", make children rebuild their
|
|
// clipping too
|
|
if (deep) {
|
|
for (child = FirstChild(); child; child = child->NextSibling())
|
|
child->RebuildClipping(true);
|
|
}
|
|
}
|
|
|
|
// add the user clipping in case there is one
|
|
if (fDrawState->HasClipping()) {
|
|
// NOTE: in case the user sets a user defined clipping region,
|
|
// rebuilding the clipping is a bit more expensive because there
|
|
// is no separate "drawing region"... on the other
|
|
// hand, views for which this feature is actually used will
|
|
// probably not have any children, so it is not that expensive
|
|
// after all
|
|
if (fUserClipping == NULL) {
|
|
fUserClipping = new (nothrow) BRegion;
|
|
if (fUserClipping == NULL)
|
|
return;
|
|
}
|
|
|
|
fDrawState->GetCombinedClippingRegion(fUserClipping);
|
|
} else {
|
|
delete fUserClipping;
|
|
fUserClipping = NULL;
|
|
}
|
|
|
|
delete fScreenAndUserClipping;
|
|
fScreenAndUserClipping = NULL;
|
|
fScreenClippingValid = false;
|
|
}
|
|
|
|
|
|
BRegion&
|
|
View::ScreenAndUserClipping(BRegion* windowContentClipping, bool force) const
|
|
{
|
|
// no user clipping - return screen clipping directly
|
|
if (fUserClipping == NULL)
|
|
return _ScreenClipping(windowContentClipping, force);
|
|
|
|
// combined screen and user clipping already valid
|
|
if (fScreenAndUserClipping != NULL)
|
|
return *fScreenAndUserClipping;
|
|
|
|
// build a new combined user and screen clipping
|
|
fScreenAndUserClipping = new (nothrow) BRegion(*fUserClipping);
|
|
if (fScreenAndUserClipping == NULL)
|
|
return fScreenClipping;
|
|
|
|
ConvertToScreen(fScreenAndUserClipping);
|
|
fScreenAndUserClipping->IntersectWith(
|
|
&_ScreenClipping(windowContentClipping, force));
|
|
return *fScreenAndUserClipping;
|
|
}
|
|
|
|
|
|
void
|
|
View::InvalidateScreenClipping()
|
|
{
|
|
// TODO: appearantly, we are calling ScreenClipping() on
|
|
// views who's parents don't have a valid screen clipping yet,
|
|
// this messes up the logic that for any given view with
|
|
// fScreenClippingValid == false, all children have
|
|
// fScreenClippingValid == false too. If this could be made the
|
|
// case, we could save some performance here with the commented
|
|
// out check, since InvalidateScreenClipping() might be called
|
|
// frequently.
|
|
// TODO: investigate, if InvalidateScreenClipping() could be
|
|
// called in "deep" and "non-deep" mode, ie. see if there are
|
|
// any cases where the children would still have valid screen
|
|
// clipping, even though the parent's screen clipping becomes
|
|
// invalid.
|
|
// if (!fScreenClippingValid)
|
|
// return;
|
|
|
|
delete fScreenAndUserClipping;
|
|
fScreenAndUserClipping = NULL;
|
|
fScreenClippingValid = false;
|
|
// invalidate the childrens screen clipping as well
|
|
for (View* child = FirstChild(); child; child = child->NextSibling()) {
|
|
child->InvalidateScreenClipping();
|
|
}
|
|
}
|
|
|
|
|
|
BRegion&
|
|
View::_ScreenClipping(BRegion* windowContentClipping, bool force) const
|
|
{
|
|
if (!fScreenClippingValid || force) {
|
|
fScreenClipping = fLocalClipping;
|
|
ConvertToScreen(&fScreenClipping);
|
|
|
|
// see if parts of our bounds are hidden underneath
|
|
// the parent, the local clipping does not account for this
|
|
IntRect clippedBounds = Bounds();
|
|
ConvertToVisibleInTopView(&clippedBounds);
|
|
if (clippedBounds.Width() < fScreenClipping.Frame().Width()
|
|
|| clippedBounds.Height() < fScreenClipping.Frame().Height()) {
|
|
BRegion* temp = fWindow->GetRegion();
|
|
if (temp) {
|
|
temp->Set((clipping_rect)clippedBounds);
|
|
fScreenClipping.IntersectWith(temp);
|
|
fWindow->RecycleRegion(temp);
|
|
}
|
|
}
|
|
|
|
fScreenClipping.IntersectWith(windowContentClipping);
|
|
fScreenClippingValid = true;
|
|
}
|
|
|
|
return fScreenClipping;
|
|
}
|
|
|
|
|
|
void
|
|
View::_MoveScreenClipping(int32 x, int32 y, bool deep)
|
|
{
|
|
if (fScreenClippingValid) {
|
|
fScreenClipping.OffsetBy(x, y);
|
|
delete fScreenAndUserClipping;
|
|
fScreenAndUserClipping = NULL;
|
|
}
|
|
|
|
if (deep) {
|
|
// move the childrens screen clipping as well
|
|
for (View* child = FirstChild(); child; child = child->NextSibling()) {
|
|
child->_MoveScreenClipping(x, y, deep);
|
|
}
|
|
}
|
|
}
|
|
|