libui/haiku/window.cpp

211 lines
5.3 KiB
C++

// 18 november 2015
#include <map>
#include "uipriv_haiku.hpp"
using namespace std;
// TODOs:
// - Command+Q invariably quits; override that by catching the B_QUIT_REQUESTED in main.cpp
// - other global shortcuts that need to be handled by overriding DispatchMessage() (NOT MessageReceived()) when adding uiArea event handling
class libuiBWindow : public BWindow {
public:
// C++11! Inherit constructors.
using BWindow::BWindow;
virtual void MessageReceived(BMessage *);
uiWindow *w;
virtual bool QuitRequested();
};
struct uiWindow {
uiHaikuControl c;
libuiBWindow *window;
BGroupLayout *vbox;
struct singleChild *child;
int margined;
int (*onClosing)(uiWindow *, void *);
void *onClosingData;
};
uiHaikuDefineControlWithOnDestroy(
uiWindow, // type name
uiWindowType, // type function
window, // handle
complain("attempt to use default CommitDestroy() code for uiWindow on Haiku"); // on destroy
)
static map<uint32, void (*)(BMessage *)> eventHandlers;
typedef map<uint32, void (*)(BMessage *)>::const_iterator eventHandlerIter;
void uiHaikuRegisterEventHandler(uint32 what, void (*handler)(BMessage *))
{
eventHandlerIter iter;
// TODO decide a convention for libui internal messages
iter = eventHandlers.find(what);
if (iter == eventHandlers.end()) { // new entry
eventHandlers[what] = handler;
return;
}
if (iter->second != handler) // mismatch
complain("attempt to clobber BMessage::what 0x%08X in uiHaikuRegisterEventHandler()", what);
}
void libuiBWindow::MessageReceived(BMessage *msg)
{
eventHandlerIter iter;
// handle registered events
iter = eventHandlers.find(msg->what);
if (iter != eventHandlers.end()) {
(*(iter->second))(msg);
return;
}
// no event found; defer to BWindow
this->BWindow::MessageReceived(msg);
}
bool libuiBWindow::QuitRequested()
{
// manually destroy the window ourselves; don't let the default BWindow code do it directly
if ((*(this->w->onClosing))(this->w, this->w->onClosingData))
uiControlDestroy(uiControl(this->w));
// don't continue to the default BWindow code; we destroyed the window by now
return false;
}
static int defaultOnClosing(uiWindow *w, void *data)
{
return 0;
}
static void windowCommitDestroy(uiControl *c)
{
uiWindow *w = uiWindow(c);
// first hide ourselves
w->window->Hide();
// now destroy the child
if (w->child != NULL)
singleChildDestroy(w->child);
// and finally destroy ourselves
// this is why we don't use the libui-provided CommitDestroy() implementation
// TODO check this for errors? or use B_QUIT_REQUESTED?
w->window->Lock();
w->window->Quit();
// w->window is now destroyed for us
}
// The default implementations assume a BView, which a uiWindow is not.
static void windowCommitShow(uiControl *c)
{
uiWindow *w = uiWindow(c);
// This is documented as behaving how we want with regards to presentation.
w->window->Show();
}
static void windowCommitHide(uiControl *c)
{
uiWindow *w = uiWindow(c);
w->window->Hide();
}
static void windowContainerUpdateState(uiControl *c)
{
uiWindow *w = uiWindow(c);
if (w->child != NULL)
singleChildUpdateState(w->child);
}
char *uiWindowTitle(uiWindow *w)
{
return uiHaikuStrdupText(w->window->Title());
}
void uiWindowSetTitle(uiWindow *w, const char *title)
{
w->window->SetTitle(title);
// don't queue resize; the caption isn't part of what affects layout and sizing of the client area (it'll be ellipsized if too long)
}
void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
{
w->onClosing = f;
w->onClosingData = data;
}
// see singlechild.cpp
static void attach(void *attachTo, BLayoutItem *what)
{
BGroupLayout *vbox = (BGroupLayout *) attachTo;
vbox->AddItem(what);
}
void uiWindowSetChild(uiWindow *w, uiControl *child)
{
if (w->child != NULL) {
w->vbox->RemoveItem(singleChildLayoutItem(w->child));
singleChildRemove(w->child);
}
w->child = newSingleChild(child, uiControl(w), attach, w->vbox);
if (w->child != NULL)
uiWindowSetMargined(w, w->margined);
}
int uiWindowMargined(uiWindow *w)
{
return w->margined;
}
void uiWindowSetMargined(uiWindow *w, int margined)
{
w->margined = margined;
if (w->child != NULL)
if (w->margined)
singleChildSetMargined(w->child, B_USE_WINDOW_SPACING);
else
singleChildSetMargined(w->child, 0);
}
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
{
uiWindow *w;
w = (uiWindow *) uiNewControl(uiWindowType());
// TODO find out how to make it ignore position
// The given rect is the size of the inside of the window, just as we want.
w->window = new libuiBWindow(
BRect(100, 100, width, height),
title,
B_TITLED_WINDOW,
// TODO B_AUTO_UPDATE_SIZE_LIMITS?
// TODO if we do this we need to set the maximum size to arbitrary (TODO always? check GTK+ and OS X)
B_ASYNCHRONOUS_CONTROLS);
w->window->w = w;
w->vbox = new BGroupLayout(B_VERTICAL, 0);
w->window->SetLayout(w->vbox);
// Haiku itself does this, with a TODO
w->vbox->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
uiWindowOnClosing(w, defaultOnClosing, NULL);
uiHaikuFinishNewControl(w, uiWindow);
uiControl(w)->CommitDestroy = windowCommitDestroy;
uiControl(w)->CommitShow = windowCommitShow;
uiControl(w)->CommitHide = windowCommitHide;
uiControl(w)->ContainerUpdateState = windowContainerUpdateState;
return w;
}