// 18 november 2015 #include #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 eventHandlers; typedef map::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; }