[haiku-development] Re: Hello, and a BBitmap question.

  • From: Stephan Assmus <superstippi@xxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Sat, 24 May 2008 13:33:43 +0200

jkim202@xxxxxxxxxxxxxxxxx wrote:
> 
> Hello Haiku developers!
> 
> My name is James and I have been working on a layout manager called ALM. 
> In particular, I have been porting it from original C# version. As you 
> will see in the lib directory, ALM is already available in Haiku. I am 
> currently working on porting an extension of ALM.
> 
> I need some help with converting a BControl (or BView) object, such as 
> BButton, to an image (BBitmap). For example, an image of BButton in a GUI 
> needs to be created and rendered in a window, replacing the original 
> BButton. Obviously, this rendered image would look the same as the 
> original BButton, but it would have no functionality because it is no 
> longer a real button.
> 
> I have looked into BBitmap in bebook and also some drawing methods such 
> as BView::DrawBitmap(). After reading those documents, my initial thought 
> was to create a BBitmap object and then add a BView as a child and draw a 
> BControl inside that BView. If my understanding is correct, everything 
> drawn inside BBitmap's child BView is drawn onto BBitmap. But I don't 
> know how to draw a BControl inside the BView. I can add a BControl inside 
> BView, but does that mean that it is actually "drawing"?
> 
> For C# users, this is done using Control.DrawToBitmap() followed by 
> Graphics.DrawImage() methods. Is this possible in Haiku?

What you want to do is possible, but is a little more tricky. I will first 
give you the reason and then the solution. In BeOS/Haiku controls are 
usually drawn asynchronously. The BWindow in which the view is embedded 
receives a message from the app_server for regions that have to be redrawn. 
Then the window calls the Draw() hook function of every affected view. This 
will dispatch drawing commands to the app_server. At the app_server side, 
the drawing commands are handled by an object that represents the rendering 
backend and is attached to a "chunk of memory". For normal windows, this 
memory is the frame buffer, for BBitmaps which can have child views this is 
the bitmaps memory. As far as the app_server is concerned, there is no 
difference between receiving and rendering drawing commands for normal 
BWindows and offscreen windows. But on the client side, there is a 
difference. A BBitmap which are supposed to contain BViews will create a 
BWindow to make the app_server connection. But the messaging thread of this 
window is never run. Therefor it cannot receive any messages from the 
app_server and the app_server will not even try to send any. So the 
connection works only in one direction. Calls like "BView::Invalidate()" 
will not have any effect. If you want a view to draw anything, you will 
have to call Draw() on this view yourself, and you will also have to wait 
for the app_server to finish executing the commands (BView::Sync()). If you 
want to render a view hierarchy, you will have to iterate recursively over 
the children of the view and call Draw() on each one. It would be nice if 
BView or BBitmap offered a method like that already. Would probably be 
nicest in the BBitmap class, I can add such a call. For the time being, you 
should do something like this:

void
DrawViewAndChildren(BView* view, BRect updateRect)
{
   int32 count = view->CountChildren();
   for (int32 i = 0; i < count; i++) {
      BView* child = view->ChildAt(i);
      BRect childUpdateRect(updateRect);
      child->ConvertFromParent(&childUpdateRect);
      DrawViewAndChildren(child, childUpdateRect);
   }
   view->Draw(updateRect);
}

Then you can draw the hierarchy like this:

if (bitmap->Lock()) {
   DrawViewAndChildren(topLevelView, topLevelView->Bounds());
   topLevelView->Sync();
   bitmap->Unlock();
}
viewAttachedToRealWindow->DrawBitmap(bitmap);

Best regards,
-Stephan



Other related posts: